打字稿类型'字符串'不能分配给类型

时间:2016-06-22 21:25:27

标签: javascript typescript angular

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

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

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

myString:string = "Banana";

myFruit:Fruit = myString;

当我这样做时

myFruit = myString;

我收到错误:

  

键入' string'不能分配类型'" Orange" | "苹果" |   "香蕉"'

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

11 个答案:

答案 0 :(得分:101)

您需要cast it

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

let myFruit: Fruit = myString as Fruit;

另请注意,使用string literals时,您只需使用一个|

答案 1 :(得分:13)

执行此操作时:

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

答案 2 :(得分:9)

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

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

例如:

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

let myFruit: Fruit = Fruit.Banana;

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

答案 3 :(得分:6)

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

在那种情况下,简单的强制转换<Fruit> fruitStringfruitString as Fruit是唯一的解决方案(请参阅其他答案)。您永远无法在编译时对此进行改进。

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


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

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

幸运的是,您定义了:

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

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

如果我的打字稿中有一个像'banana'这样的字符串,并且编译器认为,我常常会遇到“无法分配”的错误,我将其表示为字符串,而我确实想要它的类型为banana。编译器的智能程度将取决于代码的结构。

以下是我今天遇到此错误的一个示例:

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

解决该问题的另一种方法是通过投射父对象:

我的定义如下:

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

让我们说一个错误(字符串不可分配错误):

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

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

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

那我们就不必做<'invalid'> 'invalid'

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

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

答案 4 :(得分:6)

打字稿3.4引入了新的'const'断言

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

您将能够做到:

let fruit = <const> 'orange';

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

答案 5 :(得分:1)

以上所有答案均有效,但是在某些情况下,字符串文字类型是另一种复杂类型的一部分。考虑以下示例:

  // 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参数,则这很好。例如,您有一个函数可以接受或返回大小为type的参数,而您想键入它。

  // 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)
  ));

答案 6 :(得分:1)

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

原因:My type inference on myArray wasn't working correctly

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

特别感谢Reactiflux的布雷迪为这个问题提供的帮助。

答案 7 :(得分:1)

在传播此错误的数组中仍然可能会引起一些误导:

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

答案 8 :(得分:0)

例如,如果在模拟数据时要转换为dropdownvalue[],则将其组成具有值和显示属性的对象数组。

示例

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

答案 9 :(得分:0)

我遇到了同样的问题,我进行了以下更改,问题得到解决。

打开 watchQueryOptions.d.ts 文件

<nbdeploy clientUrlPart="${client.urlPart}" debugmode="false" forceRedeploy="${forceRedeploy}"/>

更改查询类型为 any 而不是 DocumentNode ,对于突变相同

之前:

\apollo-client\core\watchQueryOptions.d.ts

之后:

export interface QueryBaseOptions<TVariables = OperationVariables> {
    query: **DocumentNode**;

答案 10 :(得分:0)

我收到了相同的错误消息,但情况略有不同。我来到这里寻找答案,但没有一个答案对我有用,所以我将与其他人分享我的解决方案。我没有自定义类型,它只是一个字符串数组。我有一个带有字符串属性 abcOrD 的对象 myObject,它必须是字符串数组中的值之一,例如 "a" "b" "c" 或 "d ”。尝试分配 myObject.abcOrD = myStringVar 时,它会给我一条错误消息 Type 'string' is not assignable to type "a" | "b" | "c" | "d"。在玩弄并尝试了一些对我有用的事情之后,使用as any

myObject.abcOrD = myStringVar as any;