【问题标题】:Why isn't Typescript requiring my function to return a certain type?为什么 Typescript 不要求我的函数返回某种类型?
【发布时间】:2020-05-15 02:45:25
【问题描述】:

我有一个应该返回特定类型的通用 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 类型分配相同的“严格性”?

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

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

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

    有时人们想要这样的“精确”类型,但它们并不是语言的一部分。相反,TypeScript 拥有的是excess property checking,它只发生在非常特殊的情况下:当一个“新鲜”的对象字面量被赋予一个不知道对象字面量中某些属性的类型时:

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

    一个对象字面量是“新鲜的”,如果它还没有被分配给任何类型。 xw 之间的唯一区别是,在 x 中,文字是“新鲜的”并且禁止使用过多的属性,而在 w 中,文字是......呃......“陈旧”,因为它有已经被赋予类型FooWidget


    由此看来,您的 widgetFactory 似乎应该给出一个错误,因为您正在返回对象字面量而没有在任何地方分配它。不幸的是,在这种情况下失去了新鲜感。有一个长期存在的问题,microsoft/TypeScript#12632,注意到了这一点,并且取决于一个非常老的问题,microsoft/TypeScript#241。 TypeScript 在检查它是否与预期的返回类型兼容时会自动扩展返回的类型......并且新鲜度会丢失。看起来没人喜欢这个,但是it's hard to fix it without breaking other things。所以就目前而言,就是这样。


    您已经有了一种解决方法:显式注释函数的返回类型。这不是特别令人满意,但它完成了工作。

    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 link to code

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 2020-02-11
      • 2018-07-24
      • 1970-01-01
      • 2017-06-07
      • 1970-01-01
      相关资源
      最近更新 更多