为什么 Typescript 不要求我的函数返回某种类型? [英] Why isn't Typescript requiring my function to return a certain type?

查看:25
本文介绍了为什么 Typescript 不要求我的函数返回某种类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a generic Factory function that should return a specific type:

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

interface Widget {
  creationTime: number;
}

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

我希望 Typescript 抛出错误,因为 foo 不是接口 Widget 上的属性.然而,它没有.

I would expect Typescript to throw an error because foo is not a property on the interface Widget. However, it does not.

但是如果我将 widgetFactory 函数修改为下面的代码——唯一的区别是我明确声明了返回类型——那么它确实会抛出一个错误:

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:

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

有没有办法让 Typescript 为我的通用 Factory 类型分配相同的严格性"?

Is there a way to make Typescript assign the same "strictness" to my generic Factory type?

推荐答案

TypeScript 中的对象类型通常不会禁止额外的属性.它们是开放的"或可扩展的",而不是封闭的"或精确的"(参见 微软/TypeScript#12936).否则就不可能使用子类或接口扩展:

Object types in TypeScript do not in general prohibit extra properties. They are "open" or "extendible", as opposed to "closed" or "exact" (see 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

有时人们想要这样精确"的类型,但它们并不是语言的真正组成部分.相反,TypeScript 拥有的是多余的属性检查,这只发生在非常特殊的情况下:当新鲜"对象字面量被赋予一种不知道对象字面量中的某些属性的类型时:

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:

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

如果对象字面量尚未分配给任何类型,则它是新鲜的".xw 之间的唯一区别是 x 中的文字是新鲜的"并且禁止过多的属性,而在 w 中 文字是……呃……陈旧",因为它已经被赋予了 FooWidget 类型.

An object literal is "fresh" if it hasn't been assigned to any type yet. 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.

由此看来,您的 widgetFactory 应该会出错,因为您返回的是对象字面量而没有将其分配给任何地方.不幸的是,在这种情况下失去了新鲜感.有一个长期存在的问题,microsoft/TypeScript#12632,注意到这一点,并取决于一个非常古老的问题,microsoft/TypeScript#241.在检查它是否与预期的返回类型兼容时,TypeScript 会自动扩大返回的类型……并且新鲜度会丢失.看起来没有人喜欢这个,但是在不破坏其他事情的情况下很难修复它.所以现在,就是这样.

From that it might seem that your widgetFactory should give an error, since you are returning the object literal without assigning it anywhere. 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. TypeScript automatically widens the returned type when checking to see if it's compatible with the expected return type... and freshness is lost. 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!

游乐场链接代码

这篇关于为什么 Typescript 不要求我的函数返回某种类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆