gpt4 book ai didi

typescript - 为什么 Typescript 无法弄清楚我的代码中的类型?

转载 作者:行者123 更新时间:2023-12-03 14:06:09 26 4
gpt4 key购买 nike

为什么 Typescript 编译器会提示以下代码?

type Foo = {
a: string
}

type Bar = {
b: number
}

type Baz = Foo | Bar;

function f(x: Baz): number {
if (x.a) { // property 'a' does not exist on type Bar!
return 0;
}

if (x.b) { // property 'b' does not exist on type Foo!
return 1;
}

return -1;
}
Link to Playground

最佳答案

为什么编译器不能(或不会)允许这些属性访问
考虑本文中提到的以下案例 github thread linked in the comments by jcalz :

interface Vec2 {
x: number
y: number
}

interface Vec3 {
x: number
y: number
z: number
}

const m = { x: 0, y: 0, z: "hello world" };
const n: Vec2 = m; // N.B. structurally m qualifies as Vec2!
function f(x: Vec2 | Vec3) {
if (x.z) return x.z.toFixed(2); // This fails if z is not a number!
}
f(n); // compiler must allow this call
Playground
在这里,代码的作者做出了一个不幸的假设,即仅仅因为一个属性存在并且它是某种类型的真实。但这是双重错误:您可能有正确类型的假值(在这种情况下为零或 NaN)或不同类型的真值。
但还有一些更微妙的问题:
type Message =
{ kind: "close" } |
{ kind: "data", payload: object }

function handle(m: Message) {
switch (m.kind) {
case "close":
console.log("closing!");
// forgot 'break;' here
case "data":
updateBankAccount(m.payload);
}
}
在这种情况下,您希望编译器提示意外的属性访问,而不仅仅是默默地传播 undefined .捕获这类事情是我们首先使用静态分析的重要原因。
Typescript 编译器已经是一项了不起的工程壮举,它不仅是一种动态语言,而且是一种超动态语言,它在工程上分层静态类型系统。您在此处查找的名称是 type narrowing ,您可以在其中获取可能不止一种类型的值,然后将其缩小到特定类型。 TS 编译器支持(至少)五种不同的习惯用法来实现这一点:
  • instanceof运算符(operator)。
  • typeof运算符(operator)。
  • in运算符(operator)。
  • user-defined type guard .
  • discriminated union .

  • 让我们依次来看看:
    实例
    这个适用于用户定义的类:
    class A {
    public a: number
    constructor () {
    this.a = 4;
    }
    }

    class B {
    public b: number
    constructor () {
    this.b = 5;
    }
    }

    type AB = A | B;
    function abba(x: AB): number {
    if (x instanceof A) return x.a;
    if (x instanceof B) return x.b;
    return 0;
    }
    Playground
    类型
    这适用于 JS 原语(未定义、数字、字符串、 bool 值等)。
    type snumber = string | number;

    function f(x: snumber): string {
    if (typeof x === 'number') {
    return x.toFixed(2); // strings don't have toFixed
    } else {
    return x.repeat(2); // numbers don't have repeat
    }
    }
    Playground

    这个很适合结构类型的对象:
    type A = {
    a: number
    }

    type B = {
    b: string
    }

    type AB = A | B;

    function f(x: AB): number {
    if ('a' in x) return x.a;
    if ('b' in x) return 5;
    return 0;
    }
    Playground
    细心的读者会注意到,这与上面第一个激励示例存在相同的问题,即对象上属性的存在并不能以任何方式保证类型。这是 TS 团队的一个务实决定,允许对想要获取值或 undefined 的简单选择加入习语进行不寻常的行为。 ,并且很像类型转换是一个隐含的 promise ,即程序员对可能的结果负责。
    用户定义的类型保护
    这几乎适用于任何事情,但比之前的选项更冗长。这个直接来自 TS Handbook :
    function isFish(pet: Fish | Bird): pet is Fish { // note the 'is'
    return (pet as Fish).swim !== undefined;
    }

    let pet = getSmallPet();

    if (isFish(pet)) {
    pet.swim();
    } else {
    pet.fly();
    }
    歧视工会
    当您有一堆非常相似的对象,这些对象仅在单个属性的(静态可知!)值上有所不同时,这种方法最有效:
    type A = {
    a: string
    kind: 'is-an-a'
    }

    type B = {
    b: number
    kind: 'is-a-b'
    }

    type AB = A | B;

    function f(x: AB): string {
    switch (x.kind) {
    case 'is-an-a': return x.a;
    case 'is-a-b': return '' + x.b;
    }
    }
    请注意,正如我所说,您需要使判别式(在这种情况下为 kind 属性)成为静态已知值,通常是字符串文字或枚举的成员。您不能使用变量,因为它们的值在编译时是未知的。
    Playground
    所以总而言之,Typescript 编译器可以解决这个问题,你只需要使用一个它可以静态验证的习惯用法,而不是一个它不能验证的习惯用法,它为你提供了相当多的选项。

    关于typescript - 为什么 Typescript 无法弄清楚我的代码中的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66233214/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com