[英]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.