Il tipo di carattere 'stringa' non è assegnabile al tipo


162

Ecco cosa ho in fruit.ts

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

Ora sto importando fruit.ts in un altro file dattiloscritto. Ecco cosa ho

myString:string = "Banana";

myFruit:Fruit = myString;

Quando io faccio

myFruit = myString;

Ottengo un errore:

Digitare 'string' non è assegnabile al tipo '"Orange" | "Mela" | "Banana"'

Come posso assegnare una stringa a una variabile di tipo personalizzato Fruit?


1
Sei abbastanza sicuro dell'uso delle virgolette singole e doppie in export type Fruit?
Weather Vane,

1
@WeatherVane Ho appena controllato il mio Fruit.ts. Ho delle virgolette singole per il tipo di esportazione Fruit = 'Orange' || "Mela" || "Banana". Grazie
user6123723 il

Mi sembrano ancora delle doppie citazioni ...
Weather Vane,

Risposte:


231

Dovrai lanciarlo :

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

let myFruit: Fruit = myString as Fruit;

Si noti inoltre che quando si utilizzano valori letterali stringa, è necessario utilizzarne solo uno|

modificare

Come menzionato nell'altra risposta di @Simon_Weaver, è ora possibile affermarlo a const:

let fruit = "Banana" as const;

11
bello :) nella maggior parte dei casi const myFruit: Fruit = "Banana"farebbe.
Jacka,

Posso fare il contrario? Intendo qualcosa del genere let myFruit:Fruit = "Apple" let something:string = myFruit as string Mi sta dando un errore: la conversione del tipo 'Fruit' nel tipo 'string' potrebbe essere un errore.
Siraj Alam,

@Siraj Dovrebbe funzionare bene, non hai nemmeno bisogno della as stringparte. Ho provato il tuo codice nel parco giochi e non ci sono errori.
Nitzan Tomer,

@NitzanTomer stackoverflow.com/questions/53813188/... Si prega di guardare la mia domanda dettagliata
Siraj Alam

Ma ora se errore di battitura const myString: string = 'Bananaaa'; non ottengo errori di compilazione a causa del casting ... non c'è modo di farlo mentre si digita la stringa?
blub

67

Dattiloscritto 3.4 introduce la nuova asserzione 'const'

Ora puoi impedire che i tipi letterali (ad es. 'orange'O 'red') vengano "allargati" per scrivere stringcon un cosiddettoconst asserzione.

Sarai in grado di fare:

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

E poi non si trasformerà più in un stringaltro - che è la radice del problema nella domanda.


Per le persone che, come me, non sono ancora in 3.4. <const> può essere sostituito da qualsiasi, ma ovviamente non è pulito come questa soluzione.
Pieter De Bie,

2
Sintassi preferito sarebbe let fruit = 'orange' as const;quando seguente regola senza angolari staffa tipo affermazione
ThunderDev

2
Questa è la risposta corretta per il moderno Typescript. Impedisce l'importazione non necessaria di tipi e consente alla tipizzazione strutturale di fare correttamente le sue cose.
James McMahon,

24

Quando lo fai:

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

... si sta creando un tipo chiamato Fruitche può contenere solo i letterali "Orange", "Apple"e "Banana". Questo tipo si estende String, quindi può essere assegnato a String. Tuttavia, StringNON si estende "Orange" | "Apple" | "Banana", quindi non può essere assegnato ad esso. Stringè meno specifico . Può essere qualsiasi stringa .

Quando lo fai:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...Funziona. Perché? Perché il tipo effettivo di myStringin questo esempio è "Banana". Sì, "Banana"è il tipo . Si estende Stringquindi è assegnabile a String. Inoltre, un tipo estende un tipo di unione quando estende uno dei suoi componenti. In questo caso, "Banana"il tipo si estende "Orange" | "Apple" | "Banana"perché estende uno dei suoi componenti. Quindi, "Banana"è assegnabile a "Orange" | "Apple" | "Banana"o Fruit.


2
È divertente che tu possa effettivamente mettere <'Banana'> 'Banana'e che "lanci" una "Banana"stringa "Banana"sul tipo !!!
Simon_Weaver,

2
Ma ora puoi fare ciò <const> 'Banana'che è meglio :-)
Simon_Weaver

11

Vedo che è un po 'vecchio, ma qui potrebbe esserci una soluzione migliore.

Quando si desidera una stringa, ma si desidera che la stringa corrisponda solo a determinati valori, è possibile utilizzare enum .

Per esempio:

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

let myFruit: Fruit = Fruit.Banana;

Ora saprai che, qualunque cosa accada, myFruit sarà sempre la stringa "Banana" (o qualunque altro valore enumerabile tu scelga). Ciò è utile per molte cose, sia che si tratti di raggruppare valori simili come questo o di mappare valori di facile utilizzo su valori di facile utilizzo, il tutto facendo rispettare e limitare i valori consentiti dal compilatore.


1
È divertente perché ottengo questo problema quando provo a evitarlo. Mi sta facendo impazzire sempre più. Sto cercando di essere "javascripty" e utilizzare stringhe magiche vincolate a un tipo (anziché a un elenco) e sembra ritorcersi sempre di più e increspare tutta la mia applicazione con questo errore: - /
Simon_Weaver il

1
Ciò causa anche il problema opposto. Non puoi più farlo let myFruit: Fruit = "Banana".
Sean Burton,

11

Esistono diverse situazioni che ti daranno questo errore particolare. Nel caso dell'OP c'era un valore definito esplicitamente come una stringa . Quindi devo presumere che forse questo provenga da un menu a discesa, da un servizio Web o da una stringa JSON non elaborata.

In tal caso un cast semplice <Fruit> fruitStringo fruitString as Fruitè l'unica soluzione (vedi altre risposte). Non saresti mai in grado di migliorarlo al momento della compilazione. [ Modifica: vedi la mia altra risposta su<const> ]!

Tuttavia è molto facile imbattersi in questo stesso errore quando si usano costanti nel codice che non sono mai state pensate per essere di tipo stringa . La mia risposta si concentra su quel secondo scenario:


Prima di tutto: perché le costanti di stringa "magiche" sono spesso migliori di un enum?

  • Mi piace l'aspetto di una costante di stringa rispetto a un'enum: è compatta e "javascripty"
  • Ha più senso se il componente in uso utilizza già costanti di stringa.
  • Dover importare un "tipo enum" solo per ottenere un valore di enumerazione può essere problematico in sé
  • Qualunque cosa faccia, voglio che sia compilata in modo sicuro, quindi se aggiungo rimuovi un valore valido dal tipo di unione o lo digito male, DOVREBBE dare un errore di compilazione.

Fortunatamente quando si definisce:

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

... in realtà stai definendo un'unione di tipi in cui'missing' realtà è un tipo!

Mi capita spesso di imbattermi nell'errore "non assegnabile" se ho una stringa come 'banana'nel mio dattiloscritto e il compilatore pensa che lo intendessi come una stringa, mentre volevo davvero che fosse di tipobanana . La capacità del compilatore dipende dalla struttura del codice.

Ecco un esempio di quando ho ricevuto questo errore oggi:

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

Non appena ho scoperto che 'invalid'o 'banana'potrebbe essere un tipo o una stringa ho capito che potevo semplicemente affermare una stringa in quel tipo . Fondamentalmente lanciarlo su se stesso e dire al compilatore che non voglio che sia una stringa !

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

Quindi, cosa c'è che non va nel 'casting' FieldErrorType(o Fruit)

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

Il tempo di compilazione non è sicuro:

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

Perché? Questo è dattiloscritto, quindi <FieldErrorType>è un'affermazione e stai dicendo al compilatore che un cane è un FieldErrorType ! E il compilatore lo permetterà!

MA se fai quanto segue, il compilatore convertirà la stringa in un tipo

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

Fai attenzione agli stupidi errori di battitura come questo:

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

Un altro modo per risolvere il problema è lanciare l'oggetto genitore:

Le mie definizioni erano le seguenti:

tipo di esportazione FieldName = 'numero' | 'data di scadenza' | 'CVV'; tipo di esportazione FieldError = 'none' | 'mancante' | 'non valido'; tipo di esportazione FieldErrorType = {field: FieldName, errore: FieldError};

Diciamo che otteniamo un errore con questo (la stringa non è errore assegnabile):

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

Possiamo "affermare" l'intero oggetto in FieldErrorTypequesto modo:

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

Quindi evitiamo di doverlo fare <'invalid'> 'invalid'.

E i refusi? Non afferma<FieldErrorType> semplicemente ciò che è giusto essere di quel tipo. Non in questo caso - per fortuna il compilatore SARÀ lamentarsi se si esegue questa operazione, perché è abbastanza intelligente per sapere che è impossibile:

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

Ci possono essere sottigliezze con la modalità rigorosa. Aggiornerà la risposta dopo aver confermato.
Simon_Weaver,

1

Tutte le risposte di cui sopra sono valide, tuttavia, ci sono alcuni casi in cui il tipo letterale stringa fa parte di un altro tipo complesso. Considera il seguente esempio:

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

Hai più soluzioni per risolvere questo problema. Ogni soluzione è valida e ha i suoi casi d'uso.

1) La prima soluzione è definire un tipo per la dimensione ed esportarlo da foo.ts. Questo è utile se devi lavorare con il parametro size da solo. Ad esempio, si dispone di una funzione che accetta o restituisce un parametro di tipo size e si desidera digitarlo.

  // 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 seconda opzione è semplicemente lanciarlo sul tipo ToolbarTheme. In questo caso, non è necessario esporre l'interno di ToolbarTheme se non è necessario.

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

0

Se, dropdownvalue[]ad esempio, stai eseguendo il casting di un dato beffardo, componilo come una matrice di oggetti con valore e proprietà di visualizzazione.

esempio :

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

0

Stavo affrontando lo stesso problema, ho apportato le modifiche seguenti e il problema è stato risolto.

Apri il file watchQueryOptions.d.ts

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

Modificare il tipo di query qualsiasi invece di DocumentNode , Lo stesso per la mutazione

Prima:

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

Dopo:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **any**;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.