TypeScript:具有多个相同泛型类型参数的函数的混合行为

Oli*_*Ash 7 generics typescript

给定一个函数,该函数接收相同泛型的两个参数:

declare const fn: <T>(t1: T, t2: T) => any;
Run Code Online (Sandbox Code Playgroud)

我试图推断调用此函数时TypeScript的不同行为。

当两个参数都是不同的原语时

fn(1, 'foo'); // Error due to different types
Run Code Online (Sandbox Code Playgroud)

当两个参数都是不同的对象时

fn({ foo: 1 }, { bar: 1 }) // No error and return type is union of different types
Run Code Online (Sandbox Code Playgroud)

这两种用法为什么没有相同的行为?我希望他们的行为都一样。要么:

  • 由于类型不同而导致的错误
  • 没有错误,返回类型是不同类型的并集

其次,如果传入的参数之一是变量(而不是内联对象文字),则TypeScript的行为会再次不同:

fn({ foo: 1 }, { bar: 1 }) // No error and return type is union of different types

const obj = { foo: 1 };
fn(obj, { bar: 1 }) // Error due to different types
Run Code Online (Sandbox Code Playgroud)

同样,这两种用法为什么没有相同的行为?我希望这两种情况的行为相同。

Rya*_*ugh 1

此行为引人注目的用例来自这样的示例,其中您有一些候选类型集,没有一个是其他类型的超类型,但预期的推断类型参数 ( { a?: number, b?: number, c?: number }) 非常明显:

declare const fn: <T>(t1: T, t2: T, t3: T) => any;

fn({ a: 0, b: 1 }, { b: 2, c: 3 }, { a: 4, c: 5 });
Run Code Online (Sandbox Code Playgroud)

当参数不是对象文字时,为什么不会发生这种情况?

当您与某种 type进行绑定 时,TypeScript 无法*知道 by 指向的对象具有确切的类型- 它可能具有更多未在 中声明的属性,但最终通过子类型关系绑定到。这在实践中很常见。fooTfooTTfoo

因此,在 OP 的示例中,推断类型{ foo?: number, bar?: number }是不合理的,因为obj可能指向具有bartype 属性的对象string

*您可以在 s 周围添加更多特殊情况,const这些特殊情况是使用未对其他内容进行类型断言的对象文字初始化的,但这只会使事情变得更加不一致