Le type de type "chaîne" n'est pas attribuable au type

162

Voici ce que j'ai dans fruit.ts

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

J'importe maintenant fruit.ts dans un autre fichier dactylographié. Voici ce que j'ai

myString:string = "Banana";

myFruit:Fruit = myString;

Quand je fais

myFruit = myString;

J'obtiens une erreur:

Le type 'chaîne' n'est pas attribuable au type '"Orange" | "Apple" | "Banane"'

Comment puis-je attribuer une chaîne à une variable de type personnalisé Fruit?

user6123723
la source
1
Êtes-vous sûr de l'utilisation des guillemets simples et doubles dans export type Fruit?
Weather Vane
1
@WeatherVane Je viens de vérifier mon Fruit.ts. J'ai des guillemets simples pour le type d'exportation Fruit = 'Orange' || «Pomme» || «Banane». Merci
user6123723
On dirait toujours des guillemets doubles pour moi ...
Weather Vane

Réponses:

231

Vous devrez le lancer :

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

let myFruit: Fruit = myString as Fruit;

Notez également que lorsque vous utilisez des littéraux de chaîne, vous ne devez utiliser qu'un seul|

Éditer

Comme mentionné dans l'autre réponse de @Simon_Weaver, il est désormais possible de l'affirmer pour const:

let fruit = "Banana" as const;
Nitzan Tomer
la source
11
gentil :) dans la plupart des cas const myFruit: Fruit = "Banana"ferait l'affaire.
Jacka
Puis-je faire l'inverse? Je veux dire quelque chose comme ça. let myFruit:Fruit = "Apple" let something:string = myFruit as string Cela me donne une erreur: la conversion du type «Fruit» en type «string» peut être une erreur.
Siraj Alam
@Siraj Cela devrait fonctionner très bien, vous n'avez même pas besoin de la as stringpièce. J'ai essayé votre code dans la cour de récréation et il n'y a aucune erreur.
Nitzan Tomer
@NitzanTomer stackoverflow.com/questions/53813188/… Veuillez regarder ma question détaillée
Siraj Alam
Mais maintenant, si je fais une faute de frappe, const myString: string = 'Bananaaa'; je n'obtiens pas d'erreurs de compilation à cause du casting ... n'y a-t-il aucun moyen de le faire en vérifiant la chaîne?
blub
67

Typescript 3.4introduit la nouvelle assertion 'const'

Vous pouvez maintenant empêcher que les types littéraux (par exemple 'orange'ou 'red') ne soient «élargis» au type stringavec une prétendue constassertion.

Vous pourrez faire:

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

Et puis il ne se transformera plus en un string- qui est la racine du problème dans la question.

Simon_Weaver
la source
Pour les gens qui, comme moi, ne sont pas encore sur 3.4. <const> peut être remplacé par n'importe lequel, mais n'est bien sûr pas aussi propre que cette solution.
Pieter De Bie
2
Syntaxe preferred serait let fruit = 'orange' as const;en règle suivante sans angle de type-support affirmation
ThunderDev
2
C'est la bonne réponse pour le Typographie moderne. Il empêche l'importation inutile de types et permet au typage structurel de faire correctement son travail.
James McMahon
24

Lorsque vous faites ceci:

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

... vous créez un type appelé Fruitqui ne peut contenir que les littéraux "Orange", "Apple"et "Banana". Ce type s'étend String, il peut donc être attribué à String. Cependant, Stringne s'étend PAS "Orange" | "Apple" | "Banana", donc il ne peut pas lui être attribué. Stringest moins spécifique . Cela peut être n'importe quelle chaîne .

Lorsque vous faites ceci:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...Ça marche. Pourquoi? Parce que le type réel de myStringdans cet exemple est "Banana". Oui, "Banana"c'est le type . Il est étendu Stringdonc il est assignable à String. En outre, un type étend un type Union lorsqu'il étend l' un de ses composants. Dans ce cas "Banana",, le type s'étend "Orange" | "Apple" | "Banana"car il étend l'un de ses composants. Par conséquent, "Banana"est assignable à "Orange" | "Apple" | "Banana"ou Fruit.

André Pena
la source
2
C'est drôle que vous puissiez en fait mettre <'Banana'> 'Banana'et cela va «lancer» une "Banana"chaîne sur "Banana"le type !!!
Simon_Weaver
2
Mais maintenant, vous pouvez faire <const> 'Banana'ce qui est mieux :-)
Simon_Weaver
11

Je vois que c'est un peu vieux, mais il pourrait y avoir une meilleure solution ici.

Lorsque vous voulez une chaîne, mais que vous voulez que la chaîne ne corresponde qu'à certaines valeurs, vous pouvez utiliser des énumérations .

Par exemple:

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

let myFruit: Fruit = Fruit.Banana;

Vous saurez maintenant que quoi qu'il arrive, myFruit sera toujours la chaîne "Banana" (ou toute autre valeur énumérable que vous choisissez). Ceci est utile pour beaucoup de choses, qu'il s'agisse de regrouper des valeurs similaires comme celle-ci ou de mapper des valeurs conviviales à des valeurs conviviales pour la machine, tout en appliquant et en restreignant les valeurs autorisées par le compilateur.

Steve Adams
la source
1
C'est drôle parce que je reçois ce problème en essayant de ne pas faire ça. Cela me rend de plus en plus fou. J'essaie d'être 'javascripty' et d'utiliser des chaînes magiques contraintes à un type (au lieu d'une énumération) et cela semble se retourner de plus en plus et se répercuter dans toute mon application avec cette erreur: - /
Simon_Weaver
1
Cela pose également le problème inverse. Vous ne pouvez plus faire let myFruit: Fruit = "Banana".
Sean Burton le
11

Il existe plusieurs situations qui vous donneront cette erreur particulière. Dans le cas de l'OP, il y avait une valeur définie explicitement comme une chaîne . Je dois donc supposer que cela provient peut-être d'une liste déroulante, d'un service Web ou d'une chaîne JSON brute.

Dans ce cas, un simple casting <Fruit> fruitStringou fruitString as Fruitest la seule solution (voir les autres réponses). Vous ne pourrez jamais améliorer cela au moment de la compilation. [ Edit: Voir mon autre réponse sur<const> ]!

Cependant, il est très facile de rencontrer cette même erreur lors de l'utilisation de constantes dans votre code qui ne sont jamais destinées à être de type chaîne . Ma réponse se concentre sur ce deuxième scénario:


Tout d'abord: pourquoi les constantes de chaîne «magiques» sont-elles souvent meilleures qu'une énumération?

  • J'aime l'apparence d'une constante de chaîne par rapport à une énumération - c'est compact et 'javascripty'
  • Cela a plus de sens si le composant que vous utilisez utilise déjà des constantes de chaîne.
  • Devoir importer un `` type enum '' juste pour obtenir une valeur d'énumération peut être gênant en soi
  • Quoi que je fasse, je veux qu'il soit compilé en toute sécurité, donc si j'ajoute supprimer une valeur valide du type d'union, ou la saisir mal, cela DOIT donner une erreur de compilation.

Heureusement lorsque vous définissez:

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

... vous définissez en fait une union de types'missing'est en fait un type!

Je rencontre souvent l'erreur `` non assignable '' si j'ai une chaîne comme 'banana'dans mon dactylographié et le compilateur pense que je l'ai signifiée comme une chaîne, alors que je voulais vraiment qu'elle soit de type banana. L'intelligence du compilateur dépendra de la structure de votre code.

Voici un exemple de quand j'ai eu cette erreur aujourd'hui:

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

Dès que j'ai découvert cela 'invalid'ou qu'il 'banana'pouvait s'agir d'un type ou d'une chaîne, j'ai réalisé que je pouvais simplement affirmer une chaîne dans ce type . Essentiellement, lancez-le sur lui - même et dites au compilateur non, je ne veux pas que ce soit une chaîne !

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

Alors qu'est-ce qui ne va pas avec juste `` lancer '' vers FieldErrorType(ou Fruit)

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

Le temps de compilation n'est pas sûr:

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

Pourquoi? Il s'agit d' un texte dactylographié, donc d' <FieldErrorType>une assertion et vous dites au compilateur qu'un chien est un FieldErrorType ! Et le compilateur le permettra!

MAIS si vous faites ce qui suit, le compilateur convertira la chaîne en un type

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

Attention aux fautes de frappe stupides comme celle-ci:

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

Une autre façon de résoudre le problème consiste à convertir l'objet parent:

Mes définitions étaient les suivantes:

type d'exportation FieldName = 'nombre' | 'expirationDate' | «cvv»; type d'exportation FieldError = 'none' | «manquant» | 'invalide'; type d'exportation FieldErrorType = {champ: FieldName, erreur: FieldError};

Disons que nous obtenons une erreur avec ceci (la chaîne d'erreur non attribuable):

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

Nous pouvons `` affirmer '' l'objet entier FieldErrorTypecomme ceci:

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

Alors on évite d'avoir à faire <'invalid'> 'invalid'.

Mais qu'en est-il des fautes de frappe? Ne fonctionne pas <FieldErrorType>simplement affirmer ce qui est sur le droit d'être de ce type. Pas dans ce cas - heureusement , le compilateur FERA porter plainte si vous faites cela, car il est assez intelligent pour savoir qu'il est impossible:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Simon_Weaver
la source
Il peut y avoir des subtilités avec le mode strict. Mettra à jour la réponse après confirmation.
Simon_Weaver
1

Toutes les réponses ci-dessus sont valides, cependant, dans certains cas, le type littéral chaîne fait partie d'un autre type complexe. Prenons l'exemple suivant:

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

Vous avez plusieurs solutions pour résoudre ce problème. Chaque solution est valide et a ses propres cas d'utilisation.

1) La première solution est de définir un type pour la taille et de l'exporter depuis le fichier foo.ts. C'est bien si vous devez travailler seul avec le paramètre size. Par exemple, vous avez une fonction qui accepte ou renvoie un paramètre de type size et vous souhaitez le saisir.

  // 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 deuxième option consiste simplement à le convertir en type ToolbarTheme. Dans ce cas, vous n'avez pas besoin d'exposer l'intérieur de ToolbarTheme si vous n'en avez pas besoin.

  // 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
la source
0

Si vous effectuez un cast en un dropdownvalue[]lors de la simulation de données, par exemple, composez-le sous la forme d'un tableau d'objets avec des propriétés de valeur et d'affichage.

exemple :

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

J'étais confronté au même problème, j'ai apporté les modifications ci-dessous et le problème a été résolu.

Ouvrez le fichier watchQueryOptions.d.ts

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

Changer le type de requête any au lieu de DocumentNode , idem pour la mutation

Avant:

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

Après:

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