简体   繁体   English

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

[英]Why isn't Typescript requiring my function to return a certain type?

I have a generic Factory function that should return a specific type:我有一个通用的Factory function 应该返回一个特定的类型:

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

interface Widget {
  creationTime: number;
}

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

I would expect Typescript to throw an error because foo is not a property on the interface Widget.我希望 Typescript 会抛出错误,因为foo不是接口 Widget 上的属性。 However, it does not.但是,事实并非如此。

But if I modify the widgetFactory function to the below code -- the only difference being that I explicitly declare the return type -- then it does throw an error:但是,如果我将widgetFactory function 修改为以下代码——唯一的区别是我明确声明了返回类型——那么它确实会引发错误:

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

Is there a way to make Typescript assign the same "strictness" to my generic Factory type?有没有办法让 Typescript 为我的通用Factory类型分配相同的“严格性”?

Object types in TypeScript do not in general prohibit extra properties. TypeScript 中的 Object 类型通常不禁止额外的属性。 They are "open" or "extendible", as opposed to "closed" or "exact" (see microsoft/TypeScript#12936 ).它们是“开放的”或“可扩展的”,而不是“封闭的”或“精确的”(参见microsoft/TypeScript#12936 )。 Otherwise it would be impossible to use subclasses or interface extensions:否则就不可能使用子类或接口扩展:

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

Sometimes people want such "exact" types, but they're not really part of the language.有时人们想要这样的“精确”类型,但它们并不是语言的一部分。 Instead, what TypeScript has is excess property checking , which only happens in very particular circumstances: when a "fresh" object literal is given a type that doesn't know about some of the properties in the object literal:相反,TypeScript 有多余的属性检查,这仅在非常特殊的情况下发生:当“新鲜” object 文字被赋予不知道 ZA8CFDE6331BD59EB2AC966F8911C4 中的某些属性的类型时:

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

An object literal is "fresh" if it hasn't been assigned to any type yet.如果 object 文字尚未分配给任何类型,则它是“新鲜的”。 The only difference between x and w is that in x the literal is "fresh" and excess properties are forbidden, while in w the literal is... uh... "stale" because it has already been given the type FooWidget . xw之间的唯一区别是,在x中,文字是“新鲜的”,并且禁止使用过多的属性,而在w中,文字是……呃……“陈旧”,因为它已经被赋予了FooWidget类型。


From that it might seem that your widgetFactory should give an error, since you are returning the object literal without assigning it anywhere.由此看来,您的widgetFactory似乎应该给出一个错误,因为您正在返回 object 文字而没有在任何地方分配它。 Unfortunately, freshness is lost in this case.不幸的是,在这种情况下失去了新鲜感。 There's a longstanding issue, microsoft/TypeScript#12632 , that notes this, and depends on a very old issue, microsoft/TypeScript#241 .有一个长期存在的问题microsoft/TypeScript#12632指出了这一点,并且取决于一个非常古老的问题microsoft/TypeScript#241 TypeScript automatically widens the returned type when checking to see if it's compatible with the expected return type... and freshness is lost. TypeScript 在检查它是否与预期的返回类型兼容时会自动扩大返回的类型......并且会丢失新鲜度。 It looks like nobody likes this, but it's hard to fix it without breaking other things .看起来没有人喜欢这个,但很难在不破坏其他东西的情况下修复它 So for now, it is what it is.所以现在,它就是这样。


You already have one workaround: explicitly annotate the function's return type.您已经有了一种解决方法:显式注释函数的返回类型。 This isn't particularly satisfying, but it gets the job done.这不是特别令人满意,但它完成了工作。

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

Other workarounds involving trying to force the compiler to compute exact types are possible but significantly uglier than what you're doing:其他涉及尝试强制编译器计算精确类型的解决方法是可能的,但比你正在做的要丑得多:

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',
      };
   },
});

So I'd suggest just continuing with what you've got there.因此,我建议您继续使用您所拥有的内容。


Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM