比如说,我有一个自定义类型

export type Fruit = "apple" | "banana" | "grape";

我想确定字符串是否是水果类型的一部分.我怎样才能做到这一点?

下面的方法行不通.

let myfruit = "pear";
if (typeof myfruit === "Fruit") {
    console.log("My fruit is of type 'Fruit'");
}

任何 idea 都很感激!

推荐答案

简短回答:

不能在运行时使用typeof判断interface种类型,它们只在编译时存在.相反,你可以写一个user-defined type guard function来判断这些类型:

const fruit = ["apple", "banana", "grape"] as const;
type Fruit = (typeof fruit)[number];
const isFruit = (x: any): x is Fruit => fruit.includes(x);

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

答案如下:


您可能会对TypeScript中的值和类型之间的差异感到困惑,尤其是当它与typeof运算符有关时.正如您可能知道的,TypeScript向JavaScript添加了一个静态类型系统,并添加了that type system gets erased when the code is transpiled.TypeScript的语法是这样的:一些表达式和语句引用运行时存在的values,而其他表达式和语句引用仅在设计/编译时存在的types.值不是类型,但它们本身不是类型.重要的是,在代码中的某些地方,编译器会期望得到一个值,并在可能的情况下将其找到的表达式解释为一个值;在其他地方,编译器会期望得到一个类型,并在可能的情况下将其找到的表达式解释为一个类型.

typeof号操作员过着双重生活.表达式typeof x总是希望x是一个值,但typeof x本身可能是一个值或类型,具体取决于上下文:

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

let TypeofBar = typeof bar;行将进入JavaScript,它将在运行时使用JavaScript typeof operator并生成一个字符串.但是type TypeofBar = typeof bar;它使用TypeScript type query operator判断TypeScript分配给名为bar的值的静态类型.

在你的代码中,

let myfruit = "pear";
if (typeof myfruit === "Fruit") { // "string" === "Fruit" ?!
    console.log("My fruit is of type 'Fruit'");
}

typeof myfruit是一个值,而不是一种类型.所以它是JavaScript typeof操作符,而不是TypeScript类型的查询操作符.它将始终返回值"string";它永远不会是Fruit"Fruit".在运行时无法获得TypeScript类型查询运算符的结果,因为类型系统在运行时被擦除.你需要放弃typeof接线员.


你要做的是对照三个已知的Fruit字符串文本判断myfruit的值...比如说:

let myfruit = "pear";
if (myfruit === "apple" || myfruit === "banana" || myfruit === "grape") {
  console.log("My fruit is of type 'Fruit'");
}

很好,对吧?好吧,也许这看起来有很多多余的代码.这里有一个不那么多余的方法.首先,根据现有的文本值数组定义Fruit类型...TypeScript可以从值推断类型,但不能从类型生成值.

const fruit = ["apple", "banana", "grape"] as const;
export type Fruit = (typeof fruit)[number];

您可以验证Fruit是否与您手动定义的类型相同.然后,对于型式试验,可以使用如下user-defined type guard:

const isFruit = (x: any): x is Fruit => fruit.includes(x);

isFruit()是一个函数,它判断是否在fruit数组中找到了它的参数,如果是,则将其参数的类型缩小为Fruit.让我们看看它的工作:

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

该类型保护还让编译器知道,在if语句的"then"子句中,myfruitFruit.想象一下,如果一个函数只接受Fruit,而一个值可能是Fruit,也可能不是Fruit:

declare function acceptFruit(f: Fruit): void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";

不能直接调用该函数:

acceptFruit(myfruit); // error, myfruit might be "pear"

但在判断后,你可以在"then"子句中调用它:

if (isFruit(myfruit)) {
  acceptFruit(myfruit); // okay, myfruit is known to be "banana"
}

这大概就是为什么首先要判断自定义类型.这样你就可以做到了.


总结一下:你不能用typeof.你可以和字符串进行比较.您可以进行一些类型推断和类型保护,以消除重复代码,并从编译器中获得控制流类型分析.

Playground link to code

Typescript相关问答推荐

返回对象的TypScript构造函数隐式地替换This的值来替换super(.)的任何调用者?

Angular 17 Select 错误core.mjs:6531错误错误:NG 05105:发现意外合成监听器@transformPanel.done

如何在方法中定义TypeScript返回类型,以根据参数化类型推断变量的存在?

类型脚本接口索引签名

如何在第一次加载组件时加载ref数组

React typescribe Jest调用函数

如何在单击停止录制按钮后停用摄像机?(使用React.js和Reaction-Media-Recorder)

如何推断计算逻辑的类型

类型脚本`Return;`vs`Return unfined;`

通过字符串索引访问对象';S的值时出现文字脚本错误

缩小并集子键后重新构造类型

如何在已知基类的任何子类上使用`extends`?

为什么我在这个重载函数中需要`as any`?

错误:NG02200:找不到类型为';对象';的其他支持对象';[对象对象]';.例如数组

具有匹配功能的TS通用事件处理程序

使用来自API调用的JSON数据的Angular

TS不能自己推断函数返回类型

在Vite中将SVG导入为ReactComponent-不明确的间接导出:ReactComponent

为什么我会得到一个;并不是所有的代码路径都返回一个值0;switch 中的所有情况何时返回或抛出?(来自vsCode/TypeScript)

元组解释