Typecript Type 'string' no es asignable para escribir

162

Esto es lo que tengo en fruit.ts

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

Ahora estoy importando fruit.ts en otro archivo mecanografiado. Esto es lo que tengo

myString:string = "Banana";

myFruit:Fruit = myString;

Cuando lo hago

myFruit = myString;

Me sale un error:

El tipo 'cadena' no se puede asignar al tipo '"Naranja" | "Manzana" | "Plátano"'

¿Cómo puedo asignar una cadena a una variable de tipo personalizado Fruit?

usuario6123723
fuente
1
¿Está seguro del uso de comillas simples y dobles export type Fruit?
Veleta
1
@WeatherVane Acabo de revisar mi Fruit.ts. Tengo comillas simples para el tipo de exportación Fruta = 'Naranja' || 'Manzana "||' Plátano". Gracias
user6123723
Todavía me parecen algunas comillas dobles ...
Weather Vane

Respuestas:

231

Tendrás que lanzarlo :

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

let myFruit: Fruit = myString as Fruit;

También tenga en cuenta que cuando usa literales de cadena necesita usar solo uno|

Editar

Como se menciona en la otra respuesta de @Simon_Weaver, ahora es posible afirmarlo a const:

let fruit = "Banana" as const;
Nitzan Tomer
fuente
11
bonito :) en la mayoría de los casos const myFruit: Fruit = "Banana"lo haría.
Jacka
¿Puedo hacer lo contrario? Me refiero a algo como esto. let myFruit:Fruit = "Apple" let something:string = myFruit as string Me está dando un error: la conversión del tipo 'Fruta' al tipo 'cadena' puede ser un error.
Siraj Alam
@Siraj Debería funcionar bien, ni siquiera necesitas la as stringparte. He intentado su código en el patio de recreo y no hay errores.
Nitzan Tomer
@NitzanTomer stackoverflow.com/questions/53813188/… Por favor, mire mi pregunta detallada
Siraj Alam
Pero ahora, si escribo un error tipográfico const myString: string = 'Bananaaa'; , no obtengo errores de compilación debido a la conversión ... ¿no hay forma de hacerlo mientras escribo revisando la cadena?
blub
67

Typecript 3.4presenta la nueva afirmación 'const'

Ahora puede evitar que los tipos literales (por ejemplo, 'orange'o 'red') se 'amplíen' para escribir stringcon una llamada constaserción.

Podrás hacer:

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

Y luego ya no se convertirá en uno string, que es la raíz del problema en la pregunta.

Simon_Weaver
fuente
Para las personas que, como yo, todavía no están en 3.4. <const> puede ser reemplazado por cualquiera, pero por supuesto no es tan limpio como esta solución.
Pieter De Bie
2
La sintaxis preferida sería let fruit = 'orange' as const;cuando se sigue la regla de aserción de tipo sin ángulo de paréntesis
ThunderDev
2
Esta es la respuesta correcta para el mecanografiado moderno. Impide la importación innecesaria de tipos y permite que la tipificación estructural haga su trabajo correctamente.
James McMahon el
24

Cuando haces esto:

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

... está creando un tipo llamado Fruitque solo puede contener los literales "Orange", "Apple"y "Banana". Este tipo se extiende String, por lo tanto, se puede asignar a String. Sin embargo, StringNO se extiende "Orange" | "Apple" | "Banana", por lo que no se le puede asignar. StringEs menos específico . Puede ser cualquier cadena .

Cuando haces esto:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...funciona. ¿Por qué? Porque el tipo real de myStringen este ejemplo es "Banana". Si, "Banana"es el tipo . Se extiende Stringpor lo que es asignable a String. Además, un tipo extiende un tipo de unión cuando extiende cualquiera de sus componentes. En este caso, "Banana"el tipo se extiende "Orange" | "Apple" | "Banana"porque extiende uno de sus componentes. Por lo tanto, "Banana"es asignable a "Orange" | "Apple" | "Banana"o Fruit.

André Pena
fuente
2
¡Es curioso que puedas ponerlo <'Banana'> 'Banana'y eso 'arrojará' una "Banana"cadena al "Banana"tipo!
Simon_Weaver
2
Pero ahora puedes hacer <const> 'Banana'cuál es mejor :-)
Simon_Weaver
11

Veo que esto es un poco viejo, pero podría haber una mejor solución aquí.

Cuando desee una cadena, pero desea que la cadena solo coincida con ciertos valores, puede usar enumeraciones .

Por ejemplo:

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

let myFruit: Fruit = Fruit.Banana;

Ahora sabrá que pase lo que pase, myFruit siempre será la cadena "Banana" (o cualquier otro valor enumerable que elija). Esto es útil para muchas cosas, ya sea agrupando valores similares como este, o asignando valores fáciles de usar a valores amigables para la máquina, todo mientras impone y restringe los valores que el compilador permitirá.

Steve Adams
fuente
1
Es divertido porque tengo este problema cuando trato de evitar hacer esto. Me está volviendo cada vez más loco. Estoy tratando de ser 'javascripty' y usar cadenas mágicas restringidas a un tipo (en lugar de una enumeración) y parece ser cada vez más contraproducente y
ondular
1
Esto también causa el problema opuesto. Ya no puedes hacerlo let myFruit: Fruit = "Banana".
Sean Burton
11

Hay varias situaciones que le darán este error particular. En el caso del OP, había un valor definido explícitamente como una cadena . Entonces, debo suponer que tal vez esto vino de un menú desplegable, o servicio web o cadena JSON sin procesar.

En ese caso, un elenco simple <Fruit> fruitStringo fruitString as Fruites la única solución (ver otras respuestas). Nunca podría mejorar esto en tiempo de compilación. [ Editar: ¡Mira mi otra respuesta sobre<const> ]!

Sin embargo, es muy fácil encontrarse con este mismo error cuando se usan constantes en su código que nunca pretenden ser de tipo cadena . Mi respuesta se centra en ese segundo escenario:


En primer lugar: ¿por qué las constantes de cadena 'mágicas' a menudo son mejores que una enumeración?

  • Me gusta cómo se ve una cadena constante frente a una enumeración: es compacta y 'javascripty'
  • Tiene más sentido si el componente que está utilizando ya usa constantes de cadena.
  • Tener que importar un 'tipo de enumeración' solo para obtener un valor de enumeración puede ser problemático en sí mismo
  • Independientemente de lo que haga, quiero que sea seguro para la compilación, por lo que si agrego eliminar un valor válido del tipo de unión o escribirlo incorrectamente, DEBE dar un error de compilación.

Afortunadamente cuando define:

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

... en realidad estás definiendo una unión de tipos donde 'missing'realmente es un tipo!

A menudo me encuentro con el error 'no asignable' si tengo una cadena como 'banana'en mi mecanografiado y el compilador cree que lo dije como una cadena, mientras que realmente quería que fuera de tipo banana. Lo inteligente que pueda ser el compilador dependerá de la estructura de su código.

Aquí hay un ejemplo de cuándo recibí este error hoy:

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

Tan pronto como descubrí eso 'invalid'o 'banana'podría ser un tipo o una cadena, me di cuenta de que podía afirmar una cadena en ese tipo . Esencialmente, házlo en sí mismo y dile al compilador que no, ¡no quiero que sea una cadena !

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

¿Cuál es tan malo con sólo 'fundición' a FieldErrorType(o Fruit)

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

No es tiempo de compilación 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é? Esto es mecanografiado, así que <FieldErrorType>es una afirmación y le está diciendo al compilador que un perro es un FieldErrorType . ¡Y el compilador lo permitirá!

PERO si haces lo siguiente, el compilador convertirá la cadena a un tipo

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

Solo ten cuidado con errores estúpidos como este:

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

Otra forma de resolver el problema es lanzar el objeto padre:

Mis definiciones fueron las siguientes:

tipo de exportación FieldName = 'número' | 'expirationDate' | 'cvv'; tipo de exportación FieldError = 'none' | 'desaparecido' | 'inválido'; tipo de exportación FieldErrorType = {field: FieldName, error: FieldError};

Digamos que tenemos un error con esto (el error de cadena no asignable):

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

Podemos 'afirmar' todo el objeto de FieldErrorTypeesta manera:

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

Entonces evitamos tener que hacer <'invalid'> 'invalid'.

¿Pero qué hay de los errores tipográficos? No <FieldErrorType>solo afirma lo que está a la derecha de ser de ese tipo. No en este caso - por suerte el compilador SE quejarse si usted hace esto, porque es lo suficientemente inteligente para saber que es imposible:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Simon_Weaver
fuente
Puede haber sutilezas con el modo estricto. Actualizará la respuesta después de confirmar.
Simon_Weaver
1

Todas las respuestas anteriores son válidas, sin embargo, hay algunos casos en que el tipo literal de cadena es parte de otro tipo complejo. Considere el siguiente ejemplo:

  // 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 })
  ));

Tienes múltiples soluciones para arreglar esto. Cada solución es válida y tiene sus propios casos de uso.

1) La primera solución es definir un tipo para el tamaño y exportarlo desde foo.ts. Esto es bueno si necesita trabajar con el parámetro de tamaño por sí solo. Por ejemplo, tiene una función que acepta o devuelve un parámetro de tamaño de letra y desea escribirlo.

  // 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) La segunda opción es lanzarlo al tipo ToolbarTheme. En este caso, no necesita exponer el interno de ToolbarTheme si no lo necesita.

  // 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
fuente
0

Si, dropdownvalue[]por ejemplo, está enviando datos a una burla, compóngala como una matriz de objetos con valor y propiedades de visualización.

ejemplo :

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

Estaba enfrentando el mismo problema, hice los cambios a continuación y el problema se resolvió.

Abra el archivo watchQueryOptions.d.ts

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

Cambie el tipo de consulta any en lugar de DocumentNode , lo mismo para la mutación

Antes de:

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

Después:

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