打字稿:根据自定义类型检查“typeof”

新手上路,请多包涵

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

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

我想确定一个字符串是否属于 Fruit 类型。我怎样才能做到这一点?

以下不起作用。

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

任何想法表示赞赏!

原文由 maia 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 346
1 个回答

简短的回答:

您不能在运行时使用 typeof 来检查仅在编译时存在的 interface 类型。相反,您可以编写 用户定义的类型保护函数 来检查此类类型:

 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 添加了一个静态类型系统,并且 当代码被转译时,该类型系统会被删除。 TypeScript 的语法是这样的,一些表达式和语句引用运行时存在的 _值_,而其他表达式和语句引用仅在设计/编译时存在的 _类型_。值 具有 类型,但它们本身不是类型。重要的是,在代码中的某些地方,编译器会期望一个值并将它找到的表达式解释为一个值,如果可能的话,编译器会期望一个类型并将它找到的表达式解释为一个类型。

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 运算符 并生成一个字符串。但是 type TypeofBar = typeof bar ;被删除,它使用 TypeScript 类型查询运算符 检查 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 运算符。


可以 做的是检查 myfruit 与三个已知的 Fruit 字符串文字的值…例如,例如:

 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 与您手动定义的类型相同。然后,对于类型测试,您可以像这样使用 用户定义的类型保护

 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 的值:

 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 代码链接

原文由 jcalz 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进