ChatGPT解决这个技术问题 Extra ChatGPT

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

这是我在fruit.ts中的内容

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

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

myString:string = "Banana";

myFruit:Fruit = myString;

当我做

myFruit = myString;

我收到一个错误:

类型“字符串”不可分配给类型“橙色”| “苹果” | “香蕉”'

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


T
Top-Master

更新

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

let fruit = "Banana" as const;

旧答案

您需要cast it

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

let myFruit: Fruit = myString as Fruit;

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


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

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

您现在可以使用所谓的 const 断言防止文字类型(例如 'orange''red')被“扩展”为类型 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 来表示必须为真或假的布尔值。这有时在 discriminated unions 中很有用。当你看到它时你就会知道。


对于像我一样还没有使用 3.4 的人。 可以被任何替换,但当然不如这个解决方案那么干净。
遵循 no-angle-bracket-type-assertion 规则时,首选语法为 let fruit = 'orange' as const;
这是现代 Typescript 的正确答案。它可以防止不必要的类型导入,并让结构类型正确地完成它的工作。
A
Andre Pena

当你这样做时:

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 的实际 type"Banana"。是的,"Banana"类型。它扩展了 String,因此可以分配给 String。此外,当类型扩展任何其组件时,类型扩展联合类型。在这种情况下,类型 "Banana" 扩展了 "Orange" | "Apple" | "Banana",因为它扩展了它的一个组件。因此,"Banana" 可分配给 "Orange" | "Apple" | "Banana"Fruit


有趣的是,您实际上可以放入 <'Banana'> 'Banana',这会将 "Banana" 字符串“转换”为 "Banana" 类型!
但现在你可以做 <const> 'Banana' 哪个更好:-)
S
Simon_Weaver

有几种情况会给你这个特定的错误。在 OP 的情况下,有一个明确定义为字符串的值。所以我不得不假设这可能来自下拉列表、Web 服务或原始 JSON 字符串。

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

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

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

我喜欢字符串常量与枚举的外观——它紧凑且“javascripty”

如果您正在使用的组件已经使用字符串常量,则更有意义。

仅仅为了获取枚举值而必须导入“枚举类型”本身就很麻烦

无论我做什么,我都希望它是编译安全的,所以如果我从联合类型中删除一个有效值,或者输入错误,那么它必须给出一个编译错误。

幸运的是,当您定义:

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

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

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

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

// 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> 不只是断言 任何属于该类型的权利。在这种情况下不是 - 幸运的是,如果你这样做,编译器 WILL 会抱怨,因为它足够聪明,知道这是不可能的:

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

严格模式可能有一些微妙之处。确认后会更新答案。
S
Steve Adams

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

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

例如:

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

let myFruit: Fruit = Fruit.Banana;

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


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

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

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

b
blub

在传播此错误的数组中,仍然可能会产生一些误导:

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

A
Alfrex92

在将道具传递给 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 帮助解决这个问题。


H
Himanshu Bansal

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

例子:

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

S
Simon_Weaver

这个问题被标记为 Angular,即使它与 Angular 没有任何关系。但是,(至少)有一个 Angular 特定情况,您可能会意外收到此错误。

如果您禁用了 strictNullInputTypes

如果您使用诸如 Fruit 之类的文字类型作为 @Input()

您尝试将“橙色”传递给输入,它会被解释为字符串。

这将在 Angular 13.1 中修复。

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


X
XT_Nova

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

示例模型:

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';
}

S
SendETHToThisAddress

我收到了同样的错误消息,但场景略有不同。我来这里寻找答案,但没有一个答案对我有用,所以我会为其他人分享我的解决方案。我没有自定义类型,它只是一个字符串数组。我有一个带有字符串属性 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;

H
Himanshu Bansal

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

打开 watchQueryOptions.d.ts 文件

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

将查询类型更改为任何而不是 DocumentNode,突变相同

前:

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

后:

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