javascript 为什么Typescript不要求我的函数返回某个类型?

roejwanj  于 2023-01-16  发布在  Java
关注(0)|答案(1)|浏览(96)

我有一个泛型Factory函数,它应该返回一个特定的类型:

type Factory<T> = () => T;

interface Widget {
  creationTime: number;
}

const build: Factory<Widget> = () => {
  return {
    creationTime: Date.now(),
    foo: 'bar',
  };
};

我希望Typescript会抛出一个错误,因为foo不是接口Widget上的属性。
但是如果我将widgetFactory函数修改为下面的代码--唯一的区别是我显式声明了返回类型--那么它确实会抛出一个错误:

const build: Factory<Widget> = (): Widget => {
  return {
    creationTime: Date.now(),
    foo: 'bar',
  };
};

有没有办法让Typescript为我的泛型Factory类型分配相同的“严格性”?

4sup72z8

4sup72z81#

TypeScript中的对象类型通常不禁止额外的属性。它们是“开放的”或“可扩展的”,而不是“封闭的”或“精确的”(参见microsoft/TypeScript#12936)。否则将无法使用子类或接口扩展:

interface FooWidget extends Widget {
   foo: string;
}
const f: FooWidget = { creationTime: 123, foo: "baz" };
const w: Widget = f; // okay

有时候,人们想要这样的“精确”类型,但它们并不是语言的一部分,相反,TypeScript有多余的属性检查,这只在非常特殊的情况下才会发生:当一个“新鲜的”对象文字被赋予一个不知道对象文字中某些属性的类型时:

const x: Widget = { creationTime: 123, foo: "baz" }; // error, what's foo

对象常量是“新鲜的”,如果它还没有被赋给任何类型。xw之间的唯一区别是,在x中,常量是“新鲜的”,并且禁止多余的属性,而在w中,常量是...呃...“陈旧的”,因为它已经被赋给了FooWidget类型。
这样看来,widgetFactory应该会给予一个错误,因为您返回的是object literal,而没有将其赋给任何地方。不幸的是,在这种情况下,新鲜度丢失了。有一个长期存在的问题microsoft/TypeScript#12632注意到了这一点,并且依赖于一个非常老的问题,microsoft/TypeScript#241.TypeScript在检查返回类型是否与预期的返回类型兼容时会自动加宽返回类型...并且新鲜度会丢失。看起来没有人喜欢这样,但要想修好它而不破坏其他东西是很难的。所以现在,它就是这样。
您已经有一个解决方法:显式注解函数的返回类型。这不是特别令人满意,但它完成了任务。

export const WidgetFactory1: Factory<Widget> = {
   build: (): Widget => {
      return {
         creationTime: Date.now(),
         foo: 'bar', // error!
      };
   },
};

其他尝试强制编译器计算精确类型的变通方法也是可行的,但比您现在所做的要糟糕得多:

const exactWidgetFactory =
   <W extends Widget & Record<Exclude<keyof W, keyof Widget>, never>>(
      w: Factory<W>) => w;

export const WidgetFactory2 = exactWidgetFactory({
   build: () => { // error!
// ~~~~~ <-- types of property foo are incompatible
      return {
         creationTime: Date.now(),
         foo: 'bar',
      };
   },
});

所以我建议你继续做你手头的事。
Playground代码链接

相关问题