typescript类型“字符串”不可分配给类型

IT技术 javascript typescript angular
2021-02-18 03:52:38

这是我在fruit.ts中的内容

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

现在我在另一个typescript文件中导入fruit.ts。这是我所拥有的

myString:string = "Banana";

myFruit:Fruit = myString;

当我做

myFruit = myString;

我收到一个错误:

类型 'string' 不能分配给类型 '"Orange" | “苹果” | “香蕉”'

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

6个回答

你需要投射它

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

let myFruit: Fruit = myString as Fruit;

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

编辑

正如@Simon_Weaver 在另一个答案中提到的,现在可以断言它const

let fruit = "Banana" as const;
@usagidon 您是否尝试as const过其他答案中描述选项?
2021-04-16 03:52:38
感谢 Nitzan,确实const myFruit: Fruit = 'Bananaaa' as const;会抛出编译错误,而const myFruit: Fruit = 'Bananaaa' as Fruit;不会。Simon_Weaver 的答案应该是新接受的答案,您介意编辑您的答案以包含新的 const 断言吗?
2021-04-16 03:52:38
我可以反过来做吗?我的意思是这样的 let myFruit:Fruit = "Apple" let something:string = myFruit as string 它给了我错误:将“水果”类型转换为“字符串”类型可能是一个错误。
2021-05-02 03:52:38
不错的 :) 在大多数情况下const myFruit: Fruit = "Banana"都可以。
2021-05-07 03:52:38
@Siraj 它应该可以正常工作,您甚至不需要该as string部件。我在操场上试过你的代码,没有错误。
2021-05-15 03:52:38

Typescript3.4引入了新的“const”断言

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

您将能够:

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

然后它就不会再变成 a string- 这是问题的根源。

额外提示:您还可以使用<const> false<const> true来表示必须为真或假的布尔值。有时受歧视的工会中很有用当你看到它时你就会知道。

对于像我一样还没有使用 3.4 的人。<const> 可以用 any 代替,但当然不如这个解决方案干净。
2021-04-15 03:52:38
这是现代 Typescript 的正确答案。它可以防止不必要的类型导入,并让结构化类型正确地完成它的工作。
2021-04-18 03:52:38
let fruit = 'orange' as const;遵循 no-angle-bracket-type-assertion 规则时的首选语法
2021-05-14 03:52:38

当你这样做时:

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

...您正在创建一个名为的类型Fruit该类型只能包含文字"Orange","Apple""Banana"这种类型扩展String,因此它可以分配给String. 但是,String不扩展"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""Banana""Orange" | "Apple" | "Banana"Fruit

但现在你可以做<const> 'Banana'哪个更好:-)
2021-04-22 03:52:38
有趣的是,您实际上可以<'Banana'> 'Banana'将其“转换”为"Banana"字符串"Banana"类型!!!
2021-05-14 03:52:38

有几种情况会给您带来此特定错误。在 OP 的情况下,有一个明确定义为 string的值所以我必须假设这可能来自下拉菜单、Web 服务或原始 JSON 字符串。

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

但是,在您的代码中使用从未打算为 string 类型的常量时,很容易遇到同样的错误我的回答侧重于第二种情况:


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

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

幸运的是,当您定义:

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

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

如果'banana'我的typescript中有一个字符串,并且编译器认为我的意思是它是一个字符串,而我真的希望它是类型,我经常会遇到“不可分配”错误banana编译器的智能程度取决于您的代码结构。

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

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

当我发现'invalid'or'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!

为什么?这是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!

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

我的定义如下:

导出类型 FieldName = 'number' | '到期日期' | 'cvv'; 导出类型 FieldError = 'none' | '失踪' | '无效的'; 导出类型 FieldErrorType = { 字段:FieldName,错误:FieldError };

假设我们得到了一个错误(字符串不可分配错误):

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

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

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

然后我们避免不得不做<'invalid'> 'invalid'

但是错别字呢?<FieldErrorType>只是断言任何属于那种类型的权利。在此情况下-幸运的是编译器WILL,如果你做到这一点抱怨,因为它是足够聪明的知道这是不可能的:

  fieldErrors: [ <FieldErrorType> { field: 'number', error: 'dog' } ]
严格模式可能有一些微妙之处。确认后将更新答案。
2021-05-03 03:52:38

我看到这有点旧,但这里可能有更好的解决方案。

当您想要一个字符串,但您希望该字符串只匹配某些值时,您可以使用enums

例如:

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

let myFruit: Fruit = Fruit.Banana;

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

这很有趣,因为我在试图摆脱这样做时遇到了这个问题。它越来越让我发疯。我正在尝试成为 'javascripty' 并使用限制为类型(而不是枚举)的魔法字符串,它似乎越来越适得其反,并在我的整个应用程序中出现此错误:-/
2021-04-18 03:52:38
这也会导致相反的问题。你不能再这样做了let myFruit: Fruit = "Banana"
2021-05-14 03:52:38