Texto datilografado O tipo 'string' não pode ser atribuído ao tipo

162

Aqui está o que eu tenho em fruit.ts

export type Fruit = "Orange" | "Apple" | "Banana"

Agora estou importando fruit.ts em outro arquivo datilografado. Aqui está o que eu tenho

myString:string = "Banana";

myFruit:Fruit = myString;

Quando eu faço

myFruit = myString;

Eu recebo um erro:

O tipo 'string' não é atribuível ao tipo '"Orange" | "Maçã" | "Banana"'

Como posso atribuir uma string a uma variável do tipo personalizado Fruit?

user6123723
fonte
1
Você tem certeza do uso de aspas simples e duplas export type Fruit?
Weather Vane
1
@WeatherVane Acabei de verificar meu Fruit.ts. Tenho aspas simples para o tipo de exportação Fruit = 'Orange' || 'Maçã' || 'Banana'. Obrigado
user6123723
Ainda parece aspas duplas para mim ...
Weather Vane

Respostas:

231

Você precisará transmiti-lo :

export type Fruit = "Orange" | "Apple" | "Banana";
let myString: string = "Banana";

let myFruit: Fruit = myString as Fruit;

Observe também que, ao usar literais de string, você precisa usar apenas um|

Editar

Conforme mencionado na outra resposta de @Simon_Weaver, agora é possível afirmar que const:

let fruit = "Banana" as const;
Nitzan Tomer
fonte
11
nice :) na maioria dos casos const myFruit: Fruit = "Banana"faria.
Jacka
Posso fazer o contrário? Quero dizer algo como isto let myFruit:Fruit = "Apple" let something:string = myFruit as string Está me dando erro: A conversão do tipo 'Fruit' para o tipo 'string' pode ser um erro.
Siraj Alam
@Siraj Deve funcionar muito bem, você nem precisa da as stringpeça. Eu tentei seu código no playground e não há erros.
Nitzan Tomer
@NitzanTomer stackoverflow.com/questions/53813188/… Por favor, olhe para a minha pergunta detalhada
Siraj Alam
Mas agora, se eu digitar const myString: string = 'Bananaaa'; , não recebo erros de compilação por causa do vazamento ... não há como fazer isso enquanto verifica a string?
blub
67

Texto datilografado 3.4apresenta a nova asserção 'const'

Agora você pode impedir que tipos literais (por exemplo, 'orange'ou 'red') sejam 'ampliados' para digitar stringcom a chamada constasserção.

Você será capaz de fazer:

let fruit = 'orange' as const;  // or...
let fruit = <const> 'orange';

E então não se tornará stringmais - que é a raiz do problema na questão.

Simon_Weaver
fonte
Para pessoas que, como eu, ainda não estão no 3.4. <const> pode ser substituído por qualquer, mas é claro que não é tão limpo quanto esta solução.
Pieter De Bie
2
Sintaxe preferida seria let fruit = 'orange' as const;quando seguinte regra de não-angle-suporte do tipo-afirmação
ThunderDev
2
Esta é a resposta correta para o texto datilografado moderno. Impede a importação desnecessária de tipos e permite que a digitação estrutural faça seu trabalho corretamente.
James McMahon
24

Quando você faz isso:

export type Fruit = "Orange" | "Apple" | "Banana"

... você está criando um tipo chamado Fruitque pode conter apenas os literais "Orange", "Apple"e "Banana". Esse tipo se estende String, portanto, pode ser atribuído a String. No entanto, StringNÃO se estende "Orange" | "Apple" | "Banana", portanto, não pode ser atribuído a ele. Stringé menos específico . Pode ser qualquer string .

Quando você faz isso:

export type Fruit = "Orange" | "Apple" | "Banana"

const myString = "Banana";

const myFruit: Fruit = myString;

...funciona. Por quê? Uma vez que a real tipo de myStringneste exemplo é "Banana". Sim, "Banana"é o tipo . Ele se estende Stringpara ser atribuível a String. Além disso, um tipo estende um Tipo de União quando estende qualquer um de seus componentes. Nesse caso, "Banana"o tipo se estende "Orange" | "Apple" | "Banana"porque estende um de seus componentes. Portanto, "Banana"é atribuível a "Orange" | "Apple" | "Banana"ou Fruit.

André Pena
fonte
2
É engraçado que você possa colocar <'Banana'> 'Banana'e isso 'moldará' uma "Banana"string para "Banana"o tipo !!!
Simon_Weaver
2
Mas agora você pode fazer o <const> 'Banana'que é melhor :-)
Simon_Weaver 18/07/19
11

Vejo que isso é um pouco antigo, mas pode haver uma solução melhor aqui.

Quando você deseja uma sequência, mas deseja que ela corresponda apenas a determinados valores, pode usar enumerações .

Por exemplo:

enum Fruit {
    Orange = "Orange",
    Apple  = "Apple",
    Banana = "Banana"
}

let myFruit: Fruit = Fruit.Banana;

Agora você saberá que não importa o quê, o myFruit sempre será a string "Banana" (ou qualquer outro valor enumerável que você escolher). Isso é útil para muitas coisas, seja agrupando valores semelhantes como esse ou mapeando valores amigáveis ​​ao usuário para valores amigáveis ​​à máquina, enquanto aplica e restringe os valores permitidos pelo compilador.

Steve Adams
fonte
1
É engraçado porque eu entendo esse problema ao tentar evitar isso. Está cada vez mais me deixando louco. Eu estou tentando ser 'javascripty' e usar seqüências de caracteres mágicas restritas a um tipo (em vez de uma enumeração) e parece sair pela culatra cada vez mais e percorrer todo o meu aplicativo com este erro: - /
Simon_Weaver
1
Isso também causa o problema oposto. Você não pode mais fazer let myFruit: Fruit = "Banana".
Sean Burton
11

Existem várias situações que fornecerão esse erro específico. No caso do OP, havia um valor definido explicitamente como uma sequência . Portanto, devo assumir que talvez isso tenha sido causado por uma lista suspensa, serviço da Web ou string JSON não processada.

Nesse caso, um elenco simples <Fruit> fruitStringou fruitString as Fruité a única solução (veja outras respostas). Você nunca seria capaz de melhorar isso em tempo de compilação. [ Editar: Veja minha outra resposta sobre<const> ]!

No entanto, é muito fácil encontrar esse mesmo erro ao usar constantes no seu código que nunca pretendem ser do tipo string . Minha resposta se concentra nesse segundo cenário:


Primeiro de tudo: Por que as constantes de seqüência de caracteres 'mágicas' geralmente são melhores que um enum?

  • Eu gosto da aparência de uma constante de string versus uma enumeração - é compacta e 'javascripty'
  • Faz mais sentido se o componente que você está usando já usa constantes de string.
  • Ter que importar um 'tipo de enumeração' apenas para obter um valor de enumeração pode ser problemático por si só
  • O que quer que eu faça, quero que ele seja compilado com segurança. Se eu adicionar remover um valor válido do tipo de união ou digitar errado, DEVE dar um erro de compilação.

Felizmente, quando você define:

export type FieldErrorType = 'none' | 'missing' | 'invalid'

... na verdade você está definindo uma união de tipos onde 'missing'é realmente um tipo!

Geralmente, encontro o erro 'não atribuível' se eu tiver uma string como 'banana'no meu texto datilografado e o compilador achar que eu quis dizer isso como uma string, enquanto eu realmente queria que fosse do tipo banana. O quão inteligente o compilador é capaz dependerá da estrutura do seu código.

Aqui está um exemplo de quando recebi esse erro hoje:

// this gives me the error 'string is not assignable to type FieldErrorType'
fieldErrors: [ { fieldName: 'number', error: 'invalid' } ]

Assim que descobri isso 'invalid'ou 'banana'poderia ser um tipo ou uma string, percebi que poderia apenas afirmar uma string nesse tipo . Elenco essencial para si mesmo , e diga ao compilador não, não quero que isso seja uma string !

// so this gives no error, and I don't need to import the union type too
fieldErrors: [ { fieldName: 'number', error: <'invalid'> 'invalid' } ]

Então, o que há de errado em apenas 'transmitir' para FieldErrorType(ou Fruit)

// why not do this?
fieldErrors: [ { fieldName: 'number', error: <FieldErrorType> 'invalid' } ]

Não é um tempo de compilação seguro:

 <FieldErrorType> 'invalidddd';  // COMPILER ALLOWS THIS - NOT GOOD!
 <FieldErrorType> 'dog';         // COMPILER ALLOWS THIS - NOT GOOD!
 'dog' as FieldErrorType;        // COMPILER ALLOWS THIS - NOT GOOD!

Por quê? Isso é datilografado, então <FieldErrorType>é uma afirmação e você está dizendo ao compilador que um cachorro é um FieldErrorType ! E o compilador permitirá!

MAS se você fizer o seguinte, o compilador converterá a string em um tipo

 <'invalid'> 'invalid';     // THIS IS OK  - GOOD
 <'banana'> 'banana';       // THIS IS OK  - GOOD
 <'invalid'> 'invalidddd';  // ERROR       - GOOD
 <'dog'> 'dog';             // ERROR       - GOOD

Apenas atente para erros estúpidos como este:

 <'banana'> 'banan';    // PROBABLY WILL BECOME RUNTIME ERROR - YOUR OWN FAULT!

Outra maneira de resolver o problema é lançando o objeto pai:

Minhas definições foram as seguintes:

tipo de exportação FieldName = 'number' | 'expirationDate' | 'cvv'; tipo de exportação FieldError = 'none' | 'ausente' | 'inválido'; tipo de exportação FieldErrorType = {field: FieldName, erro: FieldError};

Digamos que obtivemos um erro com isso (a string não pode ser atribuída):

  fieldErrors: [ { field: 'number', error: 'invalid' } ]

Podemos 'afirmar' todo o objeto FieldErrorTypecomo este:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'invalid' } ]

Então evitamos ter que fazer <'invalid'> 'invalid'.

Mas e os erros de digitação? Não afirma<FieldErrorType> apenas o que está à direita para ser desse tipo. Não neste caso - felizmente o compilador VAI reclamar se você fizer isso, porque é inteligente o suficiente para saber que é impossível:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Simon_Weaver
fonte
Pode haver sutilezas no modo estrito. Atualizará a resposta após a confirmação.
Simon_Weaver
1

Todas as respostas acima são válidas, no entanto, há alguns casos em que o Tipo literal de string faz parte de outro tipo complexo. Considere o seguinte exemplo:

  // in foo.ts
  export type ToolbarTheme = {
    size: 'large' | 'small',
  };

  // in bar.ts
  import { ToolbarTheme } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}

  // Here you will get the following error: 
  // Type 'string' is not assignable to type '"small" | "large"'.ts(2322)
  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size })
  ));

Você tem várias soluções para corrigir isso. Cada solução é válida e possui seus próprios casos de uso.

1) A primeira solução é definir um tipo para o tamanho e exportá-lo do foo.ts. Isso é bom se você precisar trabalhar com o parâmetro size por conta própria. Por exemplo, você tem uma função que aceita ou retorna um parâmetro do tamanho do tipo e deseja digitá-lo.

  // in foo.ts
  export type ToolbarThemeSize = 'large' | 'small';
  export type ToolbarTheme = {
    size: ToolbarThemeSize
  };

  // in bar.ts
  import { ToolbarTheme, ToolbarThemeSize } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}
  function getToolbarSize(): ToolbarThemeSize  {/* ... */}

  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size: size as ToolbarThemeSize })
  ));

2) A segunda opção é apenas convertê-la no tipo ToolbarTheme. Nesse caso, você não precisa expor o interno do ToolbarTheme, se não precisar.

  // in foo.ts
  export type ToolbarTheme = {
    size: 'large' | 'small'
  };

  // in bar.ts
  import { ToolbarTheme } from './foo.ts';
  function useToolbarTheme(theme: ToolbarTheme) {/* ... */}

  ['large', 'small'].forEach(size => (
    useToolbarTheme({ size } as ToolbarTheme)
  ));
Saman Shafigh
fonte
0

Se você estiver convertendo para dropdownvalue[]quando estiver zombando de dados, por exemplo, componha-os como uma matriz de objetos com valor e propriedades de exibição.

exemplo :

[{'value': 'test1', 'display1': 'test display'},{'value': 'test2', 'display': 'test display2'},]
meol
fonte
0

Eu estava enfrentando o mesmo problema, fiz as alterações abaixo e o problema foi resolvido.

Abrir arquivo watchQueryOptions.d.ts

\apollo-client\core\watchQueryOptions.d.ts

Altere o tipo de consulta any em vez de DocumentNode , Same para mutação

Antes:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **DocumentNode**;

Depois de:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **any**;
Anand N
fonte