Typescript类型'string'不能分配给类型

use*_*723 96 javascript typescript angular

这就是我在fruit.ts中所拥有的

export type Fruit = "Orange" | "Apple" | "Banana"
Run Code Online (Sandbox Code Playgroud)

现在我在另一个打字稿文件中导入fruit.ts.这就是我所拥有的

myString:string = "Banana";

myFruit:Fruit = myString;
Run Code Online (Sandbox Code Playgroud)

当我做

myFruit = myString;
Run Code Online (Sandbox Code Playgroud)

我收到一个错误:

类型'字符串'不能分配给类型'"Orange"| "Apple"| "香蕉"'

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

Nit*_*mer 143

你需要施展它:

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

let myFruit: Fruit = myString as Fruit;
Run Code Online (Sandbox Code Playgroud)

另请注意,使用字符串文字时,您只需使用一个字符串文字|

  • 好的一个:)在大多数情况下`const myFruit:Fruit ="Banana"`会做的. (6认同)
  • 谢谢 Nitzan,确实 `const myFruit: Fruit = 'Bananaaa' as const;` 确实会抛出编译错误,而 `const myFruit: Fruit = 'Bananaaa' as Fruit;` 不会。Simon_Weaver 的答案应该是新接受的答案,您介意编辑您的答案以包含新的 const 断言吗? (2认同)

Sim*_*ver 29

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

现在,您可以防止将文字类型(例如'orange''red')“扩展”为string使用所谓的const断言。

您将可以执行以下操作:

let fruit = 'orange' as const;
Run Code Online (Sandbox Code Playgroud)

然后它将string不再变成一个错误-这是问题中错误的根源。

  • 遵循无角括号类型断言规则时,首选语法是`letfruit = 'orange' as const;` (2认同)
  • 这是现代 Typescript 的正确答案。它可以防止不必要的类型导入,并让结构类型正确地完成其工作。 (2认同)

And*_*ena 20

当你这样做:

export type Fruit = "Orange" | "Apple" | "Banana"
Run Code Online (Sandbox Code Playgroud)

...您正在创建一个名为类型Fruit只能包含文字"Orange","Apple""Banana".此类型扩展String,因此可以分配String.但是,String不会扩展"Orange" | "Apple" | "Banana",因此无法分配给它.String那么具体.它可以是任何字符串.

当你这样做:

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

const myString = "Banana";

const myFruit: Fruit = myString;
Run Code Online (Sandbox Code Playgroud)

...有用.为什么?因为实际的类型myString在本例中是"Banana".是的,"Banana"类型.它是扩展的,String所以它可以分配给String.此外,类型在扩展其任何组件时扩展了Union Type .在这种情况下,类型扩展,因为它扩展了其中一个组件.因此,可分配给或."Banana""Orange" | "Apple" | "Banana""Banana""Orange" | "Apple" | "Banana"Fruit

  • 但现在你可以做 `<const> 'Banana'` 更好:-) (4认同)
  • 有趣的是,你实际上可以把'<'香蕉'>'香蕉'`并将''香蕉''串起来'`香蕉'`这种类型! (2认同)

Ste*_*ams 11

我觉得这有点旧,但这里可能有更好的解决方案.

如果需要字符串,但希望字符串仅匹配某些值,则可以使用枚举.

例如:

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

let myFruit: Fruit = Fruit.Banana;
Run Code Online (Sandbox Code Playgroud)

现在你会知道,无论如何,myFruit将始终是字符串"Banana"(或者你选择的任何其他可枚举的值).这对许多事情都很有用,无论是将类似的值分组,还是将用户友好的值映射到机器友好的值,同时强制执行和限制编译器允许的值.

  • 这很有趣,因为我在试图摆脱这种情况时遇到了这个问题。它越来越让我发疯。我正在尝试成为 'javascripty' 并使用限制为类型(而不是枚举)的魔法字符串,它似乎越来越适得其反,并在我的整个应用程序中出现此错误:-/ (2认同)

Sim*_*ver 9

在几种情况下,您会遇到此特定错误。对于OP,有一个明确定义为字符串的值。因此,我必须假设这可能来自下拉列表,Web服务或原始JSON字符串。

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

但是,在代码中使用原本不打算为string类型的常量时,很容易遇到相同的错误。我的回答集中在第二种情况:


首先:为什么“魔术”字符串常量通常比枚举更好?

  • 我喜欢字符串常量与枚举的外观-它紧凑且“ javascripty”
  • 如果您正在使用的组件已经使用了字符串常量,则更有意义。
  • 仅仅为了获取枚举值而必须导入“枚举类型”本身可能很麻烦
  • 无论我做什么,我都希望它是编译安全的,因此,如果我添加从联合类型中删除有效值,或输入错误的类型,则它必须给出编译错误。

幸运的是,当您定义:

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

...您实际上是在定义类型的集,'missing'实际上是一个类型!

如果'banana'我的打字稿中有一个字符串,并且编译器认为我将其表示为字符串,那么我经常会遇到“无法分配”错误,而我确实希望它是type banana。编译器的智能程度将取决于代码的结构。

这是我今天收到此错误的示例:

// this gives me the error 'string is not assignable to type FieldErrorType'
fieldErrors: [ { fieldName: 'number', error: 'invalid' } ]
Run Code Online (Sandbox Code Playgroud)

一旦发现'invalid''banana'可能是类型或字符串,我就意识到可以将字符串声明为该类型。本质上是将其强制转换为自身,然后告诉编译器不,我不希望它是字符串

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

那么,仅“广播”到FieldErrorType(或Fruit)怎么了?

// why not do this?
fieldErrors: [ { fieldName: 'number', error: <FieldErrorType> 'invalid' } ]
Run Code Online (Sandbox Code Playgroud)

编译时间不安全:

 <FieldErrorType> 'invalidddd';  // COMPILER ALLOWS THIS - NOT GOOD!
 <FieldErrorType> 'dog';         // COMPILER ALLOWS THIS - NOT GOOD!
 'dog' as FieldErrorType;        // COMPILER ALLOWS THIS - NOT GOOD!
Run Code Online (Sandbox Code Playgroud)

为什么?这是打字稿,所以<FieldErrorType>是一个断言,您在告诉编译器一条狗是FieldErrorType!并且编译器会允许它!

但是,如果您执行以下操作,则编译器会将字符串转换为类型

 <'invalid'> 'invalid';     // THIS IS OK  - GOOD
 <'banana'> 'banana';       // THIS IS OK  - GOOD
 <'invalid'> 'invalidddd';  // ERROR       - GOOD
 <'dog'> 'dog';             // ERROR       - GOOD
Run Code Online (Sandbox Code Playgroud)

只是要注意像这样的愚蠢错别字:

 <'banana'> 'banan';    // PROBABLY WILL BECOME RUNTIME ERROR - YOUR OWN FAULT!
Run Code Online (Sandbox Code Playgroud)

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

我的定义如下:

导出类型FieldName ='数字'| 'expirationDate'| 'cvv'; 导出类型FieldError ='none'| '丢失'| '无效'; 导出类型FieldErrorType = {字段:FieldName,错误:FieldError};

假设我们对此产生了错误(字符串不可分配错误):

  fieldErrors: [ { field: 'number', error: 'invalid' } ]
Run Code Online (Sandbox Code Playgroud)

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

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'invalid' } ]
Run Code Online (Sandbox Code Playgroud)

然后,我们不必这样做<'invalid'> 'invalid'

但是错别字呢?不<FieldErrorType>只是断言无论是在右边是此类型。并非在这种情况下-幸运的是,如果您这样做,编译器抱怨,因为它足够聪明,知道它是不可能的:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
Run Code Online (Sandbox Code Playgroud)