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


161

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

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

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

myString:string = "Banana";

myFruit:Fruit = myString;

当我做

myFruit = myString;

我收到一个错误:

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

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


1
您是否确定在中使用单引号和双引号export type Fruit
风向标

1
@WeatherVane刚刚检查了我的Fruit.ts。我在其中的出口类型Fruit ='Orange'||中有单引号。'苹果” ||“香蕉”。谢谢
user6123723'6

对我来说,看起来还是有些双引号...
天气叶片

Answers:


231

您需要强制转换

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

let myFruit: Fruit = myString as Fruit;

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

编辑

如@Simon_Weaver的另一个答案中所述,现在可以将其声明为const

let fruit = "Banana" as const;

11
一个不错的:)在大多数情况下const myFruit: Fruit = "Banana"都可以。
Jacka

我可以逆转吗?我的意思是这样的,这 let myFruit:Fruit = "Apple" let something:string = myFruit as string 给我带来了错误:将“水果”类型转换为“字符串”类型可能是一个错误。
Siraj Alam

@Siraj它应该可以正常工作,您甚至不需要该as string部分。我在操场上尝试过您的代码,没有错误。
Nitzan Tomer

@NitzanTomer stackoverflow.com/questions/53813188/...请看看我的详细问题
斯拉吉阿拉姆

但是现在,如果输入错误, const myString: string = 'Bananaaa'; 我不会因为强制转换而出现编译错误...在对字符串进行类型检查时没有办法这样做吗?
blub19年

66

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

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

您将可以执行以下操作:

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

然后它不会再变成string现实-这是问题中问题的根源。


对于像我一样尚未使用3.4的人。<const>可以被任何替换,但是当然不如该解决方案那么干净。
彼得·德比

2
let fruit = 'orange' as const;当遵循no-angle-bracket-type-assertion规则时
首选

2
这是现代打字稿的正确答案。它可以防止不必要的类型导入,并让结构化类型正确执行其任务。
詹姆斯·麦克马洪

24

执行此操作时:

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
您实际上可以放置这很有趣<'Banana'> 'Banana',它将“投射” "Banana"字符串"Banana"为该类型!
Simon_Weaver

2
但现在您可以做<const> 'Banana'的更好:-)
Simon_Weaver

11

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

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

例如:

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

let myFruit: Fruit = Fruit.Banana;

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


1
这很有趣,因为在尝试摆脱此问题时遇到了这个问题。它越来越使我发疯。我正在尝试成为“ javascripty”并使用约束为一种类型的魔术字符串(而不是枚举),并且似乎越来越适得其反,并在整个应用程序中引起如下错误:-/
Simon_Weaver

1
这也引起相反的问题。您不能再这样做let myFruit: Fruit = "Banana"
肖恩·伯顿

11

在几种情况下,您会遇到此特定错误。对于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' } ]

一旦我发现'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 = {field:FieldName,错误:FieldError};

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

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

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

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

这样我们就不必做<'invalid'> 'invalid'

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

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

严格模式可能会有些微妙。确认后将更新答案。
Simon_Weaver

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

0

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

例如

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

0

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

打开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**;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.