Typescript交集类型和函数签名不会抛出预期的错误

Fmr*_*bio 4 typescript

我已声明以下类型:

type ExampleA = {
    a: string;
}

type ExampleB = {
    b: number;
}

type ExampleC = {
    c: boolean;
}

type Examples = ExampleA &
    ExampleB &
    ExampleC;
Run Code Online (Sandbox Code Playgroud)

然后我使用如下类型:

function foo(pattern: { [key: string]: string }) {
    console.log(pattern);
}

const bar: Examples = { a: 'foo', b: 1, c: false }; 
foo(bar);
Run Code Online (Sandbox Code Playgroud)

打字稿编译器在调用foo(bar)方法时不会抛出任何错误,即使bar:Examples变量与函数签名不匹配foo.

游乐场链接

打字稿为什么不抛出任何错误?这是编译器中的错误吗?

Sef*_*efe 5

其工作原理是交集类型对其基类型的可分配性.

作为交集类型,Examples可分配给ExampleA.ExampleA可分配给{ [key: string]: string }.因此,Examples必须可分配给函数参数类型

这可以在以下代码中显示:

const bar: Examples = { a: 'foo', b: 1, c: false }; 
const bar2: ExampleA = bar;
const bar3: { [key: string]: string } = bar2;
foo(bar3); //This works
foo(bar2); //Since the assignment bar3 = bar2 works, this must work, too
foo(bar); //Since the assignment bar2 = bar works, this must work, too
Run Code Online (Sandbox Code Playgroud)

游乐场版


UPDATE

当你想要坚持"当A可分配给B而B可分配给C,然后A 必须可分配给C" 时,该行为是重要的.类型系统除了允许这些类型的配置外别无选择.但是,将值作为参数传递给实际上存在另一个问题foo.

您可以将值分配给仅共享指定值的一部分成员的类型的变量.所以这个分配工作正常:

let item: { a: string, b: number } = { a: "Hello World!", b: 1 };
let partiallyMatchingItem: { a: string } = item;
Run Code Online (Sandbox Code Playgroud)

partiallyMatchingItem具有比该类型中实际声明的更多属性绝对没有问题.担保是最低保证.

但是,对映射类型的赋值不起作用,因为item类型的附加成员number:

let item = { a: "Hello World!", b: 1 };
let mappedTypeItem: { [key: string]: string } = item; //Error
Run Code Online (Sandbox Code Playgroud)

所以这次保证不是最低保证,绝对是保证.这是非常荒谬的,当你考虑你有多容易绕过它(有意或无意):

let item = { a: "Hello World!", b: 1 };
let partiallyMatchingItem: { a: string } = item;
let mappedTypeItem: { [key: string]: string } = partiallyMatchingItem;
Run Code Online (Sandbox Code Playgroud)

或者干脆:

let item = { a: "Hello World!", b: 1 };
let mappedTypeItem: { [key: string]: string } = item as { a: string };
Run Code Online (Sandbox Code Playgroud)

这是一个等待发生的错误,特别是当您枚举属性时,mappedTypeItem并假设所有属性的值都是a string.

考虑到TypeScript中常见的结构类型赋值,这种绝对保证不适合类型系统通常提供的最小保证系统.

一个干净的解决方案是使"常规"类型的值不能分配给映射类型(如果需要向后兼容性,可以使用tsconfig.json文件中的开关切换它).至少你应该避免这种分配,因为这里提供的类型安全性非常弱.