这是我的水果。ts

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

现在我在进口水果。Ts在另一个typescript文件中。这是我有的

myString:string = "Banana";

myFruit:Fruit = myString;

当我这样做的时候

myFruit = myString;

我得到一个错误:

类型“string”不能赋值给类型“Orange”|“Apple”| “香蕉”

如何将字符串分配给自定义类型水果的变量?


当前回答

有几种情况会给出这种特定的错误。在OP的情况下,有一个显式定义为字符串的值。我必须假设这可能来自下拉列表,或者web服务,或者原始JSON字符串。

在这种情况下,一个简单的类型转换<Fruit> fruitString或fruitString as Fruit是唯一的解决方案(参见其他答案)。您永远无法在编译时对此进行改进。[编辑:查看我关于<const>]的其他答案!

然而,当在代码中使用不打算为字符串类型的常量时,很容易遇到同样的错误。我的回答集中在第二种情况:


首先:为什么'神奇'字符串常量往往比enum更好?

我喜欢字符串常量相对于枚举的方式——它紧凑而“javascript” 如果您正在使用的组件已经使用字符串常量,则更有意义。 为了获得枚举值而导入“枚举类型”本身就很麻烦 无论我做什么,我希望它是编译安全的,所以如果我添加从联合类型中删除有效值,或键入错误,那么它必须给出一个编译错误。

幸运的是当你定义:

导出类型FieldErrorType = 'none' | '缺少' | '无效'

...你实际上是在定义一个类型的联合,其中'missing'实际上是一个类型!

我经常遇到'not assignable'错误,如果我在我的typescript中有一个像'banana'这样的字符串,编译器认为我的意思是它是一个字符串,而我真的想让它是香蕉类型。编译器的智能程度取决于代码的结构。

下面是我今天得到这个错误的一个例子:

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

一旦我发现'无效'或'香蕉'可以是一个类型或字符串,我意识到我可以断言一个字符串为该类型。本质上是将其转换为自身,并告诉编译器不,我不希望这是一个字符串!

// 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!

为什么?这是typescript,所以<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!

另一种解决这个问题的方法是强制转换父对象:

我的定义如下:

   export type FieldName = 'number' | 'expirationDate' | 'cvv';
   export type FieldError = 'none' | 'missing' | 'invalid';
   export type FieldErrorType = { field: FieldName, error: FieldError };

假设我们得到了一个错误(string not assignable error):

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

我们可以像这样“断言”整个对象为FieldErrorType:

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

然后我们避免必须执行<'invalid'> 'invalid'。

那么错别字呢?<FieldErrorType>不只是断言右边的任何类型为该类型。在这种情况下不会——幸运的是,如果你这样做,编译器会报错,因为它足够聪明,知道这是不可能的:

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

其他回答

这个问题被标记为Angular,尽管它实际上与Angular没有任何关系。然而,有(至少)一个Angular特定的情况,你可能会意外地得到这个错误。

如果你禁用了strictNullInputTypes 如果你使用一个文字类型,比如Fruit作为@Input() 你试图将'Orange'传递给一个输入,它被解释为一个字符串。

这将在Angular 13.1中修复。

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

例如,如果在模拟数据时转换为下拉值[],则将其组合为具有value和display属性的对象数组。

例子:

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

有几种情况会给出这种特定的错误。在OP的情况下,有一个显式定义为字符串的值。我必须假设这可能来自下拉列表,或者web服务,或者原始JSON字符串。

在这种情况下,一个简单的类型转换<Fruit> fruitString或fruitString as Fruit是唯一的解决方案(参见其他答案)。您永远无法在编译时对此进行改进。[编辑:查看我关于<const>]的其他答案!

然而,当在代码中使用不打算为字符串类型的常量时,很容易遇到同样的错误。我的回答集中在第二种情况:


首先:为什么'神奇'字符串常量往往比enum更好?

我喜欢字符串常量相对于枚举的方式——它紧凑而“javascript” 如果您正在使用的组件已经使用字符串常量,则更有意义。 为了获得枚举值而导入“枚举类型”本身就很麻烦 无论我做什么,我希望它是编译安全的,所以如果我添加从联合类型中删除有效值,或键入错误,那么它必须给出一个编译错误。

幸运的是当你定义:

导出类型FieldErrorType = 'none' | '缺少' | '无效'

...你实际上是在定义一个类型的联合,其中'missing'实际上是一个类型!

我经常遇到'not assignable'错误,如果我在我的typescript中有一个像'banana'这样的字符串,编译器认为我的意思是它是一个字符串,而我真的想让它是香蕉类型。编译器的智能程度取决于代码的结构。

下面是我今天得到这个错误的一个例子:

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

一旦我发现'无效'或'香蕉'可以是一个类型或字符串,我意识到我可以断言一个字符串为该类型。本质上是将其转换为自身,并告诉编译器不,我不希望这是一个字符串!

// 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!

为什么?这是typescript,所以<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!

另一种解决这个问题的方法是强制转换父对象:

我的定义如下:

   export type FieldName = 'number' | 'expirationDate' | 'cvv';
   export type FieldError = 'none' | 'missing' | 'invalid';
   export type FieldErrorType = { field: FieldName, error: FieldError };

假设我们得到了一个错误(string not assignable error):

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

我们可以像这样“断言”整个对象为FieldErrorType:

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

然后我们避免必须执行<'invalid'> 'invalid'。

那么错别字呢?<FieldErrorType>不只是断言右边的任何类型为该类型。在这种情况下不会——幸运的是,如果你这样做,编译器会报错,因为它足够聪明,知道这是不可能的:

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

当传递道具到React组件时,我也遇到了类似的问题。

原因:我在myArray上的类型推断没有正确工作

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

特别感谢Reactiflux的Brady在这个问题上的帮助。

Typescript 3.4引入了新的'const'断言

您现在可以防止文字类型(例如。'橙色'或'红色')被“拓宽”为使用所谓的const断言的类型字符串。

你将能够做到:

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

然后它就不会再变成字符串了,这就是问题的根源。

你也可以在整个对象上这样做:

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

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

额外提示:你也可以使用<const> false或<const> true来表示一个必须为真或假的布尔值。这在有歧视的工会中有时是有用的。你一看到就知道了。