Typoskript Der Typ 'string' kann dem Typ nicht zugewiesen werden

161

Folgendes habe ich inruit.ts

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

Jetzt importiere ich obst.ts in eine andere Typoskriptdatei. Hier ist was ich habe

myString:string = "Banana";

myFruit:Fruit = myString;

Wenn ich es tue

myFruit = myString;

Ich erhalte eine Fehlermeldung:

Der Typ 'string' kann nicht dem Typ '"Orange" | zugewiesen werden "Apple" | "Banane"'

Wie kann ich einer Variablen vom benutzerdefinierten Typ Fruit eine Zeichenfolge zuweisen?

user6123723
quelle
1
Sind Sie sich ziemlich sicher über die Verwendung von einfachen und doppelten Anführungszeichen in export type Fruit?
Wetterfahne
1
@WeatherVane Habe gerade meine Fruit.ts überprüft. Ich habe dort einfache Anführungszeichen für den Exporttyp Fruit = 'Orange' || "Apfel" || "Banane". Vielen Dank
user6123723
Sieht für mich immer noch nach doppelten Anführungszeichen aus ...
Wetterfahne

Antworten:

231

Du musst es besetzen :

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

let myFruit: Fruit = myString as Fruit;

Beachten Sie auch, dass Sie bei der Verwendung von Zeichenfolgenliteralen nur eines verwenden müssen|

Bearbeiten

Wie in der anderen Antwort von @Simon_Weaver erwähnt, ist es jetzt möglich, dies zu behaupten const:

let fruit = "Banana" as const;
Nitzan Tomer
quelle
11
schön :) würde in den meisten Fällen const myFruit: Fruit = "Banana"reichen.
Jacka
Kann ich das Gegenteil tun? Ich meine so etwas. let myFruit:Fruit = "Apple" let something:string = myFruit as string Es gibt mir einen Fehler: Die Konvertierung des Typs 'Fruit' in den Typ 'string' kann ein Fehler sein.
Siraj Alam
@ Siraj Es sollte gut funktionieren, du brauchst nicht einmal das as stringTeil. Ich habe Ihren Code auf dem Spielplatz ausprobiert und es gibt keine Fehler.
Nitzan Tomer
@NitzanTomer stackoverflow.com/questions/53813188/… Bitte schauen Sie sich meine detaillierte Frage an
Siraj Alam
Aber jetzt, wenn ich Tippfehler const myString: string = 'Bananaaa'; mache, erhalte ich keine Kompilierungsfehler aufgrund des Castings ... gibt es keine Möglichkeit, dies zu tun, während ich die Zeichenfolge überprüfe?
Blub
66

Typescript 3.4führt die neue 'const'-Behauptung ein

Sie können jetzt verhindern, dass Literaltypen (z. B. 'orange'oder 'red') erweitert werden, um stringmit einer sogenannten constBehauptung zu tippen .

Sie werden in der Lage sein:

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

Und dann wird es sich nicht stringmehr selbst zu einem - was die Wurzel des Problems in der Frage ist.

Simon_Weaver
quelle
Für Leute, die wie ich noch nicht auf 3.4 sind. <const> kann durch jedes ersetzt werden, ist aber natürlich nicht so sauber wie diese Lösung.
Pieter De Bie
2
Bevorzugte Syntax wäre, let fruit = 'orange' as const;wenn die No-Angle-Bracket-Type-Assertion-Regel
befolgt wird
2
Dies ist die richtige Antwort für modernes Typoskript. Es verhindert den unnötigen Import von Typen und lässt strukturelle Typisierung seine Sache richtig machen.
James McMahon
24

Wenn Sie dies tun:

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

... Sie erstellen einen Typ namens Fruit, der nur die Literale enthalten kann "Orange", "Apple"und "Banana". Dieser Typ wird erweitert Stringund kann daher zugewiesen werden String. Wird Stringjedoch NICHT erweitert "Orange" | "Apple" | "Banana", kann es daher nicht zugewiesen werden. Stringist weniger spezifisch . Es kann eine beliebige Zeichenfolge sein .

Wenn Sie dies tun:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...Es klappt. Warum? Da die tatsächliche Art des myStringin diesem Beispiel ist "Banana". Ja, "Banana"ist der Typ . Es ist erweitert, Stringso dass es zuweisbar ist String. Zusätzlich ist eine Art erstreckt sich eine Art Union , wenn es sich jeder seiner Komponenten. In diesem Fall wird "Banana"der Typ erweitert, "Orange" | "Apple" | "Banana"weil er eine seiner Komponenten erweitert. Daher "Banana"ist zuweisbar an "Orange" | "Apple" | "Banana"oder Fruit.

André Pena
quelle
2
Es ist lustig, dass man es tatsächlich sagen kann <'Banana'> 'Banana'und das wird eine "Banana"Zeichenfolge für "Banana"den Typ "werfen" !!!
Simon_Weaver
2
Aber jetzt können Sie tun, <const> 'Banana'was besser ist :-)
Simon_Weaver
11

Ich sehe, das ist ein bisschen alt, aber hier könnte es eine bessere Lösung geben.

Wenn Sie eine Zeichenfolge möchten, die Zeichenfolge jedoch nur mit bestimmten Werten übereinstimmen soll, können Sie Aufzählungen verwenden .

Beispielsweise:

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

let myFruit: Fruit = Fruit.Banana;

Jetzt wissen Sie, dass myFruit immer die Zeichenfolge "Banana" ist (oder was auch immer Sie für einen anderen Wert wählen). Dies ist für viele Dinge nützlich, sei es, ähnliche Werte wie diese zu gruppieren oder benutzerfreundliche Werte maschinenfreundlichen Werten zuzuordnen, während gleichzeitig die vom Compiler zugelassenen Werte erzwungen und eingeschränkt werden.

Steve Adams
quelle
1
Es ist lustig, weil ich dieses Problem bekomme, wenn ich versuche, davon wegzukommen. Es macht mich zunehmend verrückt. Ich versuche, 'javascripty' zu sein und magische Zeichenfolgen zu verwenden, die auf einen Typ beschränkt sind (anstelle einer Aufzählung), und es scheint immer mehr nach hinten
loszugehen
1
Dies verursacht auch das gegenteilige Problem. Das kannst du nicht mehr let myFruit: Fruit = "Banana".
Sean Burton
10

Es gibt verschiedene Situationen, in denen dieser Fehler auftritt. Im Fall des OP wurde ein Wert explizit als Zeichenfolge definiert . Ich muss also davon ausgehen, dass dies möglicherweise von einem Dropdown, einem Webdienst oder einer rohen JSON-Zeichenfolge stammt.

In diesem Fall ist eine einfache Besetzung <Fruit> fruitStringoder fruitString as Fruitdie einzige Lösung (siehe andere Antworten). Zum Zeitpunkt der Kompilierung könnten Sie dies niemals verbessern. [ Bearbeiten: Siehe meine andere Antwort über<const> ]!

Es ist jedoch sehr einfach, auf denselben Fehler zu stoßen, wenn Sie Konstanten in Ihrem Code verwenden, die niemals vom Typ string sein sollen . Meine Antwort konzentriert sich auf dieses zweite Szenario:


Zuallererst: Warum sind 'magische' String-Konstanten oft besser als eine Aufzählung?

  • Ich mag die Art und Weise, wie eine String-Konstante im Vergleich zu einer Aufzählung aussieht - sie ist kompakt und "javascripty".
  • Sinnvoller, wenn die von Ihnen verwendete Komponente bereits Zeichenfolgenkonstanten verwendet.
  • Das Importieren eines 'Aufzählungstyps', nur um einen Aufzählungswert zu erhalten, kann an sich problematisch sein
  • Was auch immer ich tue, ich möchte, dass es kompiliersicher ist. Wenn ich also einen gültigen Wert aus dem Union-Typ entferne oder falsch schreibe, MUSS es einen Kompilierungsfehler geben.

Zum Glück, wenn Sie definieren:

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

... Sie definieren tatsächlich eine Vereinigung von Typen, wobei 'missing'es sich tatsächlich um einen Typ handelt!

Ich stoße oft auf den Fehler 'nicht zuweisbar', wenn ich eine Zeichenfolge wie 'banana'in meinem Typoskript habe und der Compiler meint, ich hätte sie als Zeichenfolge gemeint, während ich wirklich wollte, dass sie vom Typ ist banana. Wie intelligent der Compiler sein kann, hängt von der Struktur Ihres Codes ab.

Hier ist ein Beispiel, wann ich diesen Fehler heute erhalten habe:

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

Sobald ich das herausfand, 'invalid'oder 'banana'entweder eine Art oder eine Zeichenfolge sein könnte mir klar , ich konnte es einfach assert einen String in dieser Art . Wirf es im Wesentlichen auf sich selbst und sage dem Compiler, nein, ich möchte nicht, dass dies eine Zeichenfolge ist !

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

Also, was ist falsch daran, nur zu FieldErrorType(oder Fruit) zu "gießen"?

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

Es ist nicht sicher für die Kompilierung:

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

Warum? Dies ist ein Typoskript, also <FieldErrorType>eine Behauptung, und Sie sagen dem Compiler, dass ein Hund ein FieldErrorType ist ! Und der Compiler wird es zulassen!

ABER wenn Sie Folgendes tun, konvertiert der Compiler die Zeichenfolge in einen Typ

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

Achten Sie auf solche dummen Tippfehler:

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

Eine andere Möglichkeit, das Problem zu lösen, besteht darin, das übergeordnete Objekt umzuwandeln:

Meine Definitionen waren wie folgt:

Exporttyp FieldName = 'Nummer' | 'expirationDate' | 'cvv'; Exporttyp FieldError = 'none' | 'fehlt' | 'ungültig'; Exporttyp FieldErrorType = {Feld: FieldName, Fehler: FieldError};

Nehmen wir an, wir erhalten einen Fehler (der nicht zuweisbare String-Fehler):

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

Wir können das ganze Objekt so "behaupten" FieldErrorType:

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

Dann müssen wir das nicht tun <'invalid'> 'invalid'.

Aber was ist mit Tippfehlern? Nicht <FieldErrorType>nur behaupten , was auch immer auf der rechten Seite dieser Art zu sein. Nicht in diesem Fall - zum Glück des Compiler WILL beschweren , wenn Sie dies tun, weil sie klug genug , um zu wissen , es ist unmöglich:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Simon_Weaver
quelle
Es kann Feinheiten mit striktem Modus geben. Aktualisiert die Antwort nach Bestätigung.
Simon_Weaver
1

Alle oben genannten Antworten sind gültig. In einigen Fällen ist der String-Literal-Typ jedoch Teil eines anderen komplexen Typs. Betrachten Sie das folgende Beispiel:

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

Sie haben mehrere Lösungen, um dies zu beheben. Jede Lösung ist gültig und hat ihre eigenen Anwendungsfälle.

1) Die erste Lösung besteht darin, einen Typ für die Größe zu definieren und ihn aus foo.ts zu exportieren. Dies ist gut, wenn Sie mit dem Größenparameter selbst arbeiten müssen. Sie haben beispielsweise eine Funktion, die einen Parameter der Typgröße akzeptiert oder zurückgibt, und Sie möchten ihn eingeben.

  // 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) Die zweite Option besteht darin, es einfach in den Typ ToolbarTheme umzuwandeln. In diesem Fall müssen Sie das interne ToolbarTheme nicht verfügbar machen, wenn Sie es nicht benötigen.

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

Wenn Sie dropdownvalue[]beispielsweise beim Verspotten von Daten ein Casting durchführen, erstellen Sie es als Array von Objekten mit Wert- und Anzeigeeigenschaften.

Beispiel :

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

Ich hatte das gleiche Problem, habe die folgenden Änderungen vorgenommen und das Problem wurde behoben.

Öffnen Sie die Datei watchQueryOptions.d.ts

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

Ändern Sie den Abfragetyp any anstelle von DocumentNode . Gleich für Mutation

Vor:

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

Nach dem:

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