对于合并对象中的可选属性(例如选项),类型推断可能是错误的

问题描述 投票:0回答:1

最近在使用TypeScript时遇到以下情况,在实践中会经常出现:

interface Options {
  foo?: number;
  bar?: string;
}

const defaultOptions = {
  foo: 123,
  bar: 'abc',
} as const satisfies Options;

function f(options: Options) {
  const fullOptions = { // Type is inferred as {foo: number; bar: string} (regardless of satisfies)
    ...defaultOptions,
    ...options,
  } as const satisfies Required<Options>; // This is not guaranteed and should be marked as error!
  // The actual type is still just Options because the props of defaultOptions may be overwritten.

  console.log(fullOptions);

  return fullOptions.bar.toUpperCase();
}

f({foo: 456}); // Works
f({foo: 456, bar: undefined}) // Runtime error!

游乐场链接

正如你所看到的,TypeScript 并没有在

satisfies Required<Options>
部分抱怨,尽管它应该抱怨。这是故意行为还是错误?

typescript type-inference
1个回答
0
投票

TypeScript 的行为符合预期;请参阅 microsoft/TypeScript#57408 获取权威答案。

一般来说,对于可选属性,TypeScript 不会区分缺失的属性和存在

undefined
的属性。对于很多目的来说这都很好。对于
const opts = {foo: 456}
const opts = {foo: 456, bar: undefined}
opts.bar
都是
undefined
。但在某些情况下,存在明显的差异,对象传播的行为就是这样一种情况。

很长一段时间,事情就是这样。但很多人都不满意,microsoft/TypeScript#13195 上的功能请求得到了很多社区支持。最终,TypeScript 引入了

--exactOptionalPropertyTypes
编译器选项,如在microsoft/TypeScript#43947中实现的那样。启用该编译器选项后,如果您从可选属性中
read
,您仍然会看到 undefined ,但如果您尝试将
undefined
to 写入可选属性,则会出现错误(除非
undefined
被明确指定)包含在其值类型中)。如果我们启用它,那么以下代码会产生编译器警告:

f({foo: 456, bar: undefined}) // error!
// Argument of type '{ foo: number; bar: undefined; }' is not assignable to 
// parameter of type 'Options' with 'exactOptionalPropertyTypes: true'. 
// Type 'undefined' is not assignable to type 'string'.

并且您不会意外地使用

undefined
覆盖现有属性。


因此,如果您真的关心这一点,您可能需要启用

--exactOptionalPropertyTypes
。请注意,此编译器选项包含在
--strict
编译器选项套件
中。最初这将是
--strictOptionalProperties
并包含在内。但如果您阅读 microsoft/TypeScript#44421 上的讨论,您会发现它被认为是一个重大更改。问题在于,现实世界中有大量代码(包括 TypeScript 自己的库)使用可选属性。但由于
{x?: string}
{x?: string | undefined}
在历史上是等价的,那么像
{x?: string}
这样的现有声明会发生什么情况呢?原作者是打算允许还是不允许
undefined
?没有办法以编程方式判断。这意味着有人必须仔细检查每一个可选属性并做出决定。否则,TypeScript 的下一个版本将在所有地方强制执行“disallow
undefined
”,并且很多事情都会被破坏。

因此,无论好坏,他们将其从

--strict
中移出,在
microsoft/TypeScript#44626
重命名为 --exactOptionalPropertyTypes,并告诉人们使用它需要自行承担风险。也许有一天,生态系统将准备好将该标志包含在
--strict
中,但是直到这种情况发生之前,您需要决定您自己的代码中的收益是否大于风险。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.