簡體   English   中英

Typescript 類型“字符串”不可分配給類型

[英]Typescript Type 'string' is not assignable to type

這是我在 fruit.ts 中的內容

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

現在我在另一個 typescript 文件中導入 fruit.ts。 這就是我所擁有的

myString:string = "Banana";

myFruit:Fruit = myString;

當我做

myFruit = myString;

我得到一個錯誤:

類型 'string' 不能分配給類型 '"Orange" | “蘋果” | “香蕉”'

如何將字符串分配給自定義類型 Fruit 的變量?

更新

正如@Simon_Weaver 的回答中所提到的,從 TypeScript 3.4 版開始,可以將其斷言為const

let fruit = "Banana" as const;

舊答案

你需要投射它

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

let myFruit: Fruit = myString as Fruit;

另請注意,在使用字符串文字時,您只需要使用一個|

Typescript 3.4引入了新的“const”斷言

您現在可以防止文字類型(例如'orange''red' )被“擴展”以使用所謂的const斷言鍵入string

您將能夠:

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

然后它不會再把自己變成一個string ——這是問題的根源。

您也可以對整個對象執行此操作:

let animals = [ { species: 'dog' }, { species: 'cat' } ] as const;

type firstAnimal = (typeof animals)[0]['species'];  // string literal 'dog'

額外提示:您還可以使用<const> false<const> true來表示必須為 true 或 false 的布爾值。 這有時在受歧視的工會中很有用。 當你看到它時你就會知道。

當你這樣做時:

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

...您正在創建一個名為Fruit的類型,它只能包含文字"Orange""Apple""Banana" 這種類型擴展了String ,因此它可以分配給String 但是, String不會擴展"Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana" ,所以不能分配給它。 String不太具體 它可以是任何字符串

當你這樣做時:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...有用。 為什么? 因為本例中myString的實際類型"Banana" 是的, "Banana"類型 它是擴展String所以它可以分配給String 此外,當一個類型擴展任何組件時,它會擴展聯合類型。 在這種情況下, "Banana"類型擴展了"Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana" ,因為它擴展了它的一個組件。 因此, "Banana"可分配給"Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana" "Orange" | "Apple" | "Banana"Fruit

有幾種情況會給你這個特定的錯誤。 在 OP 的情況下,有一個明確定義為 string的值。 所以我不得不假設這可能來自下拉列表、Web 服務或原始 JSON 字符串。

在這種情況下,一個簡單的演員<Fruit> fruitStringfruitString as Fruit是唯一的解決方案(見其他答案)。 您將永遠無法在編譯時對此進行改進。 [編輯:請參閱我關於<const>的其他答案]!

但是,在代碼中使用不打算為 string 類型的常量時,很容易遇到同樣的錯誤。 我的回答集中在第二種情況:


首先:為什么“魔術”字符串常量通常比枚舉更好?

  • 我喜歡字符串常量與枚舉的外觀——它緊湊且“javascripty”
  • 如果您正在使用的組件已經使用字符串常量,則更有意義。
  • 僅僅為了獲取枚舉值而必須導入“枚舉類型”本身就很麻煩
  • 無論我做什么,我都希望它是編譯安全的,所以如果我從聯合類型中刪除一個有效值,或者輸入錯誤,那么它必須給出一個編譯錯誤。

幸運的是,當您定義:

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

...您實際上是在定義一個類型的聯合,其中'missing'實際上是一種類型!

如果我的打字稿中有一個像'banana'這樣的字符串,並且編譯器認為我的意思是一個字符串,而我真的希望它是banana類型,我經常會遇到 'not assignable' 錯誤。 編譯器的智能程度取決於代碼的結構。

這是我今天收到此錯誤的示例:

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

當我發現'invalid''banana'可能是類型或字符串時,我意識到我可以將字符串斷言到該類型中。 本質上將其轉換為自身,並告訴編譯器不,我不希望這是一個字符串

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

那么僅“強制轉換”到FieldErrorType (或Fruit )有什么問題

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

這不是編譯時安全的:

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

為什么? 這是打字稿,所以<FieldErrorType>是一個斷言,你告訴編譯器狗是 FieldErrorType 編譯器將允許它!

但是如果您執行以下操作,那么編譯器會將字符串轉換為類型

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

請注意像這樣的愚蠢錯別字:

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

解決問題的另一種方法是強制轉換父對象:

我的定義如下:

導出類型字段名 = '數字' | '過期日期' | 'cvv'; 導出類型 FieldError = 'none' | '失蹤' | '無效的'; 導出類型 FieldErrorType = { 字段:字段名稱,錯誤:字段錯誤 };

假設我們得到一個錯誤(字符串不可分配錯誤):

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

我們可以像這樣將整個對象“斷言”為FieldErrorType

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

然后我們避免必須做<'invalid'> 'invalid'

但是錯別字呢? <FieldErrorType>不只是斷言右邊的任何內容都屬於該類型。 在這種情況下不是 - 幸運的是,如果你這樣做,編譯器會抱怨,因為它足夠聰明,知道這是不可能的:

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

我看到這有點舊,但這里可能有更好的解決方案。

當您想要一個字符串,但您希望該字符串只匹配某些值時,您可以使用enums

例如:

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

let myFruit: Fruit = Fruit.Banana;

現在您將知道無論如何,myFruit 將始終是字符串“Banana”(或您選擇的任何其他可枚舉值)。 這對很多事情都很有用,無論是將類似的值分組,還是將用戶友好的值映射到機器友好的值,同時強制和限制編譯器允許的值。

以上所有答案都是有效的,但是,在某些情況下,字符串文字類型是另一種復雜類型的一部分。 考慮以下示例:

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

您有多種解決方案來解決此問題。 每個解決方案都是有效的,並且有自己的用例。

1) 第一個解決方案是定義大小的類型並從 foo.ts 中導出。 如果您需要自己使用 size 參數,這很好。 例如,您有一個函數接受或返回大小類型的參數,並且您想輸入它。

  // 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) 第二個選項是將其轉換為 ToolbarTheme 類型。 在這種情況下,如果不需要,則不需要暴露 ToolbarTheme 的內部。

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

在傳播此錯誤的數組中,仍然可能會產生一些誤導:

export type Fruit = "Orange" | "Apple" | "Banana"
export type FruitArray = Fruit[];

const someFruits= ["Banana"];

const workingFruits: FruitArray = ["Orange", "Apple"]; // Works

// Even orange and apple show: Type 'string' is not assignable to type Fruit
const brokenAllFruits: FruitArray = [...someFruits, "Orange", "Apple"]; 

// As const is needed in the spread array
const someConstFruits= ["Banana" as const];
const workingAllFruits: FruitArray = [...someConstFruits, "Orange", "Apple"]; // Works

在將道具傳遞給 React 組件時,我遇到了類似的問題。

原因: My type inference on myArray wasn't working correctly

https://codesandbox.io/s/type-string-issue-fixed-z9jth?file=/src/App.tsx

特別感謝 Reactiflux 的 Brady 幫助解決這個問題。

例如,如果在模擬數據時要轉換為dropdownvalue[] ,請將其組合為具有 value 和 display 屬性的對象數組。

示例

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

這個問題被標記為 Angular,即使它與 Angular 沒有任何關系。 但是,(至少)有一個 Angular 特定情況,您可能會意外收到此錯誤。

  • 如果您禁用strictNullInputTypes
  • 如果您使用諸如Fruit之類的文字類型作為@Input()
  • 您嘗試將'Orange'傳遞給輸入,它會被解釋為字符串。

這將在 Angular 13.1 中修復。

https://github.com/angular/angular/pull/38305

如果您正在使用類,您可以執行以下操作之一:

示例模型:

type Fruit = 'Apple' | 'Banana';

interface ClassWithFruit  {
  fruit: Fruit;
}

實現模型的類,這里有三個選項:

class MyClass implements ClassWithFruit {
  // option 1
  fruit = 'Apple' as const;

  // option 2
  fruit = <const>'Apple';
  
  // option 3
  readonly fruit = 'Apple';
}

我收到了同樣的錯誤消息,但場景略有不同。 我來這里尋找答案,但沒有一個答案對我有用,所以我會為其他人分享我的解決方案。 我沒有自定義類型,它只是一個字符串數組。 我有一個帶有字符串屬性abcOrD的對象myObject ,它必須是字符串數組中的值之一,例如“a”“b”“c”或“d”。 當嘗試分配myObject.abcOrD = myStringVar時,它會給我一條錯誤消息Type 'string' is not assignable to type "a" | "b" | "c" | "d" Type 'string' is not assignable to type "a" | "b" | "c" | "d" Type 'string' is not assignable to type "a" | "b" | "c" | "d" 在玩耍並嘗試了一些對我有用的東西之后,就像 any 一樣

myObject.abcOrD = myStringVar as any;

我遇到了同樣的問題,我做了以下更改,問題得到了解決。

打開watchQueryOptions.d.ts文件

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

更改查詢類型any而不是DocumentNode ,突變相同

前:

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

后:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM