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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37978528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:55:05  来源:igfitidea点击:

Typescript Type 'string' is not assignable to type

javascripttypescriptangular

提问by user6123723

Here's what I have in fruit.ts

这是我在fruit.ts中的内容

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

Now I'm importing fruit.ts in another typescript file. Here's what I have

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

myString:string = "Banana";

myFruit:Fruit = myString;

When I do

当我做

myFruit = myString;

I get an error:

我收到一个错误:

Type 'string' is not assignable to type '"Orange" | "Apple" | "Banana"'

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

How can I assign a string to a variable of custom type Fruit?

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

回答by Nitzan Tomer

You'll need to cast it:

你需要投射它

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

let myFruit: Fruit = myString as Fruit;

Also notice that when using string literalsyou need to use only one |

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

Edit

编辑

As mentioned in the other answer by @Simon_Weaver, it's now possible to assert it to const:

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

let fruit = "Banana" as const;

回答by Simon_Weaver

Typescript 3.4introduces the new 'const' assertion

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

You can now prevent literal types (eg. 'orange'or 'red') being 'widened' to type stringwith a so-called constassertion.

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

You will be able to do:

您将能够:

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

And then it won't turn itself into a stringanymore - which is the root of the problem in the question.

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

回答by André Pena

When you do this:

当你这样做时:

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

...you are creating a type called Fruitthat can only contain the literals "Orange", "Apple"and "Banana". This type extends String, hence it can be assigned to String. However, Stringdoes NOT extend "Orange" | "Apple" | "Banana", so it cannot be assigned to it. Stringis less specific. It can be any string.

...您正在创建一个名为的类型Fruit,该类型只能包含文字"Orange","Apple""Banana"。这种类型扩展String,因此它可以分配给String. 但是,String不扩展"Orange" | "Apple" | "Banana",因此不能分配给它。String不太确切的。它可以是任何字符串

When you do this:

当你这样做时:

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

const myString = "Banana";

const myFruit: Fruit = myString;

...it works. Why? Because the actual typeof myStringin this example is "Banana". Yes, "Banana"is the type. It is extends Stringso it's assignable to String. Additionally, a type extendsa Union Type when it extends anyof its components. In this case, "Banana", the type, extends "Orange" | "Apple" | "Banana"because it extends one of its components. Hence, "Banana"is assignable to "Orange" | "Apple" | "Banana"or Fruit.

...有用。为什么?因为实际的类型myString在本例中是"Banana"。是的,"Banana"类型。它是扩展的,String所以它可以分配给String. 此外,当类型扩展任何组件时,类型会扩展联合类型。在这种情况下,"Banana"类型扩展,"Orange" | "Apple" | "Banana"因为它扩展了它的一个组件。因此,"Banana"可分配给"Orange" | "Apple" | "Banana"Fruit

回答by Steve Adams

I see this is a little old, but there might be a better solution here.

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

When you want a string, but you want the string to only match certain values, you can use enums.

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

For example:

例如:

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

let myFruit: Fruit = Fruit.Banana;

Now you'll know that no matter what, myFruit will always be the string "Banana" (Or whatever other enumerable value you choose). This is useful for many things, whether it be grouping similar values like this, or mapping user-friendly values to machine-friendly values, all while enforcing and restricting the values the compiler will allow.

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

回答by Simon_Weaver

There are several situations that will give you this particular error. In the case of the OP there was a value defined explicitly as a string. So I have to assume that maybe this came from a dropdown, or web service or raw JSON string.

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

In that case a simple cast <Fruit> fruitStringor fruitString as Fruitis the only solution (see other answers). You wouldn't ever be able to improve on this at compile time. [Edit: See my other answer about <const>] !

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

However it's very easy to run into this same error when using constants in your code that aren't ever intended to be of type string. My answer focuses on that second scenario:

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



First of all: Why are 'magic' string constants often better than an enum?

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

  • I like the way a string constant looks vs. an enum - it's compact and 'javascripty'
  • Makes more sense if the component you're using already uses string constants.
  • Having to import an 'enum type' just to get an enumeration value can be troublesome in itself
  • Whatever I do I want it to be compile safeso if I add remove a valid value from the union type, or mistype it then it MUST give a compile error.
  • 我喜欢字符串常量与枚举的对比方式——它紧凑且“javascripty”
  • 如果您使用的组件已经使用字符串常量,则更有意义。
  • 必须导入“枚举类型”才能获得枚举值本身就很麻烦
  • 无论我做什么,我都希望它是编译安全的,所以如果我添加从联合类型中删除一个有效值,或者输入错误,那么它必须给出编译错误。

Fortunately when you define:

幸运的是,当您定义:

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

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

...you're actually defining a union of typeswhere 'missing'is actually a type!

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

I often run into the 'not assignable' error if I have a string like 'banana'in my typescript and the compiler thinksI meant it as a string, whereas I really wanted it to be of type banana. How smart the compiler is able to be will depend on the structure of your code.

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

Here's an example of when I got this error today:

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

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

As soon as I found out that 'invalid'or 'banana'could be either a type or a string I realized I could just assert a string into that type. Essentially cast it to itself, and tell the compiler no I don't want this to be a string!

一旦我发现'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' } ]

So what's wrong with just 'casting' to FieldErrorType(or Fruit)

那么仅“投射”到FieldErrorType(或Fruit)有什么问题

// why not do this?
fieldErrors: [ { fieldName: 'number', error: <FieldErrorType> 'invalid' } ]

It's not compile time safe:

这不是编译时安全的:

 <FieldErrorType> 'invalidddd';  // COMPILER ALLOWS THIS - NOT GOOD!
 <FieldErrorType> 'dog';         // COMPILER ALLOWS THIS - NOT GOOD!
 'dog' as FieldErrorType;        // COMPILER ALLOWS THIS - NOT GOOD!

Why? This is typescript so <FieldErrorType>is an assertion and you are telling the compiler a dog is a FieldErrorType! And the compiler will allow it!

为什么?这是打字稿,所以<FieldErrorType>是断言,您告诉编译器一只狗是 FieldErrorType!编译器会允许的!

BUT if you do the following, then the compiler will convert the string to a type

但是,如果您执行以下操作,则编译器会将字符串转换为类型

 <'invalid'> 'invalid';     // THIS IS OK  - GOOD
 <'banana'> 'banana';       // THIS IS OK  - GOOD
 <'invalid'> 'invalidddd';  // ERROR       - GOOD
 <'dog'> 'dog';             // ERROR       - GOOD

Just watch out for stupid typos like this:

请注意像这样的愚蠢的错别字:

 <'banana'> 'banan';    // PROBABLY WILL BECOME RUNTIME ERROR - YOUR OWN FAULT!

Another way to solve the problem is by casting the parent object:

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

My definitions were as follows:

我的定义如下:

export type FieldName = 'number' | 'expirationDate' | 'cvv'; export type FieldError = 'none' | 'missing' | 'invalid'; export type FieldErrorType = { field: FieldName, error: FieldError };

导出类型 FieldName = 'number' | '到期日期' | 'cv'; 导出类型 FieldError = 'none' | '失踪' | '无效的'; 导出类型 FieldErrorType = { field: FieldName, error: FieldError };

Let's say we get an error with this (the string not assignable error):

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

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

We can 'assert' the whole object as a FieldErrorTypelike this:

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

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

Then we avoid having to do <'invalid'> 'invalid'.

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

But what about typos? Doesn't <FieldErrorType>just assertwhatever is on the right to be of that type. Not in this case - fortunately the compiler WILLcomplain if you do this, because it's clever enough to know it's impossible:

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

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

回答by Saman Shafigh

All the above answers are valid, however, there are some cases that the String Literal Type is part of another complex type. Consider the following example:

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

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

You have multiple solutions to fix this. Each solution is valid and has its own use cases.

您有多种解决方案来解决此问题。每个解决方案都是有效的,并且都有自己的用例。

1) The first solution is to define a type for the size and export it from the foo.ts. This is good if when you need to work with the size parameter by its own. For example, you have a function that accepts or returns a parameter of type size and you want to type it.

1)第一种解决方案是为size定义一个类型并从foo.ts中导出。如果您需要单独使用 size 参数,这很好。例如,您有一个接受或返回类型为 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) The second option is to just cast it to the type ToolbarTheme. In this case, you don't need to expose the internal of ToolbarTheme if you don't need.

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

回答by meol

If you're casting to a dropdownvalue[]when mocking data for example, compose it as an array of objects with value and display properties.

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

example:

例子

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

回答by Anand N

I was facing the same issue, I made below changes and the issue got resolved.

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

Open watchQueryOptions.d.tsfile

打开watchQueryOptions.d.ts文件

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

Change the query type anyinstead of DocumentNode, Same for mutation

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

Before:

前:

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

After:

后:

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