繁体   English   中英

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

[英]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 的变量?

更新

正如@Simon_Weaver 的回答中所提到的,从 TypeScript 3.4 版开始,可以将其断言为const

let fruit = "Banana" as const;

旧答案

你需要投射它

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

let myFruit: Fruit = myString as 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> fruitStringfruitString as Fruit是唯一的解决方案(见其他答案)。 您将永远无法在编译时对此进行改进。 [编辑:请参阅我关于<const>的其他答案]!

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


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

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

幸运的是,当您定义:

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 中修复。

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

如果您正在使用类,您可以执行以下操作之一:

示例模型:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM