[英]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 的變量?
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> fruitString
或fruitString as Fruit
是唯一的解決方案(見其他答案)。 您將永遠無法在編譯時對此進行改進。 [編輯:請參閱我關於<const>
的其他答案]!
但是,在代碼中使用不打算為 string 類型的常量時,很容易遇到同樣的錯誤。 我的回答集中在第二種情況:
首先:為什么“魔術”字符串常量通常比枚舉更好?
幸運的是,當您定義:
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 中修復。
如果您正在使用類,您可以執行以下操作之一:
示例模型:
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.