打字稿:对照自定义类型检查“ typeof”

时间:2018-07-25 22:57:48

标签: typescript types

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

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

我想确定字符串是否属于Fruit类型。我该怎么做?

以下内容无效。

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

任何想法都感激!

3 个答案:

答案 0 :(得分:24)

您可能会对TypeScript中的值和类型之间的差异感到困惑,特别是因为它与typeof运算符有关。如您所知,TypeScript将静态类型系统添加到JavaScript和that type system gets erased when the code is transpiled。 TypeScript的语法是,某些表达式和语句引用在运行时存在的,而其他表达式和语句引用仅在设计/编译时存在的 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运算符。


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

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

完美,对吧?好吧,也许这似乎是很多冗余代码。这是一种不太冗余的方法。首先,根据现有的文字值数组定义Fruit类型。TypeScript可以从值中推断类型,但不能从类型中生成值。

const stringLitArray = <L extends string>(arr: L[]) => arr;
const fruit = stringLitArray(["apple", "banana", "grape"]);
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,也可能不是:

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。您可以与字符串进行比较。您可以进行一些类型推断和类型防护,以消除重复的代码并从编译器获取控制流类型分析。

希望有帮助。祝你好运。

答案 1 :(得分:2)

typeof 在 TS 中:

TS 中的 typeof 运算符可用于 2 种不同的上下文:

  1. 在表达式/值上下文中返回其类型的字符串。这只是 JavaScript typeof 运算符,编译后将保留。
  2. 在类型上下文中使类型类似于现有表达式/值。这是一个 TS 结构,可帮助我们更轻松地使用某些类型表达自己。这将被编译掉,不会出现在已编译的 JavaScript 中

示例:

表达/价值上下文

const hi = 'hi';
const one = 1;
const obj = {};

console.log(typeof hi, typeof 1, typeof obj);
// [LOG]: "string",  "number",  "object"

输入上下文:

const obj1 = {foo: 1, bar: true};
const obj2 = {foo: 1, bar: ''};

// test has the type according to the structure of obj1
const test: typeof obj1 = {foo: 1, bar: true};
// typeof obj1 is the same as:
type sameAsTypeofObj1 = {foo: number, bar: string}


// test2 has the type according to the structure of obj1
const test2: typeof obj2 = {foo: 1, bar: true};
// In test2 we get a compile error since bar is not correct
// Since the type of obj2 is {foo: number, bar: string} we get the error:
// Type 'boolean' is not assignable to type 'string'

对于您的具体问题,我认为您应该使用用户定义的类型保护。下面是一个例子:

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

let myfruit = "apple";

// user defined type guard
function isFruit(fruit: string): fruit is Fruit {
  return ["apple", "banana", "grape"].indexOf("fruit") !== -1;
}

if (isFruit(myfruit)) {
    // if this condition passes 
    // then TS compiler knows that myfruit is of the Fruit type
    myfruit
}

答案 2 :(得分:0)

因此 jcalz 的答案非常好,并且实际上可以完美地回答问题。但是有一种更简单的方法,不需要使用typeof

您实际上可以使用instanceof获得相同的结果。因此,在您的情况下:

if (myfruit instanceof Fruit) {
// code goes here
}