当函数参数类型不匹配时,为什么 TypeScript 不会抛出错误?

Bag*_*ong 6 javascript typescript

这是一个非常基本的示例来演示我的意思:

type Payload = {
    id: number;
}

type GreatPayload = {
    id: number;
    surprise: 4;
}

type Action = (payload: Payload) => void;

const action: Action = payload => null;

const payload: GreatPayload = {
    id: 1,
    surprise: 4,
};

action({ id: 1, surprise: 4 }); // <== as expected, this errors out because `surprise` is not present in `Payload`

action(payload); // <== my question is, why does this not throw an error?
Run Code Online (Sandbox Code Playgroud)

(以及可编辑示例的TypeScript 游乐场链接。)

action(payload)payload传入 ( GreatPayload) 的类型与函数参数类型明显不匹配时,为什么不抛出错误Payload

jca*_*alz 8

TypeScript 中的对象类型是开放/可扩展的,而不是封闭/精确的。这意味着类型对象X包含比X提及定义更多的属性是可以接受的。您可以将对象类型定义视为描述该类型的已知属性,而对可能的未知属性没有任何影响。

这种开放性很重要,因为它允许接口扩展和类继承。您的类型定义几乎与

interface Payload {
    id: number;
}

interface GreatPayload extends Payload {
    surprise: 4;
}
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到这GreatPayload 一种特殊类型的Payload. 它有一个额外的属性,但它仍然是一个Payload. 与类继承相同的事情:

class Foo {
    a = "foo";
}
class Bar extends Foo {
    b = "bar";
}
Run Code Online (Sandbox Code Playgroud)

一个Bar实例一个Foo

const f: Foo = new Bar(); // okay
Run Code Online (Sandbox Code Playgroud)

TypeScript 编译器将对象类型视为精确的唯一地方是当您创建全新的对象文字并将其分配给类型时。这在 TypeScript 手册中记录为“ Excess Property Checks ”...,您还可以查看microsoft/TypeScript#3755,讨论此行为需要的 GitHub 问题;如果没有像这样的某种键检查,拼写错误的可选属性将是完全未捕获的错误。但这不是精确类型的完整实现。


所以当你调用它时:

action({ id: 1, surprise: 4 });  // error
Run Code Online (Sandbox Code Playgroud)

您正在传入一个包含意外surprise属性的新对象文字,编译器会通过多余的属性检查发出警告。但是当你调用它时:

action(payload);  // okay
Run Code Online (Sandbox Code Playgroud)

您正在传入变量payload,它本身不是对象文字,并且您分配给的对象文字payload不再是“新鲜的”。因此,不会发生过多的财产检查,您也不会收到警告。


如果你真的想看到实现的确切类型以便你可以轻松地请求Exact<Payload>,你可能想要去microsoft/TypeScript#12936并给它一个 ,如果它特别引人注目,甚至可能描述你的用例。

但是考虑到当前的行为可能暂时不会有任何进展,您的时间可能最好花在尝试使用开放类型而不是对抗它们上。考虑编写您的代码,以便它不会介意一个对象是否具有比类型声明中指定的更多的属性。如果你只是用已知的键索引对象,你会没事的。如果您正在遍历对象属性,如果您的代码会因意外属性而爆炸,请不要使用Object.keys()for..in循环。相反,请考虑从硬编码数组中迭代已知键(有关执行此操作的一种方法,请参阅此答案)。这个想法是让你的代码不受未知的额外属性的影响,这样你就不会在乎有人在你什么GreatPayload时候给你一个Payload.

好的,希望有帮助;祝你好运!

Playground 链接到代码