【问题标题】:Is there an option to make spreading an object strict?是否可以选择严格传播对象?
【发布时间】:2019-12-13 08:31:39
【问题描述】:

我想知道是否有编译器选项或类似的东西可以使传播对象变得严格。

请看下面的例子来理解我的意思:

interface Foo {
  a: string;
}

interface Bar {
  a: string;
  b: number;
}

const barObject: Bar = { a: "a string", b: 1 };

// should give a warning because Bar has more properties (here b) than Foo
const fooObject: Foo = { ...barObject };

// actually prints 1
console.log((fooObject as any).b); 

这样的事情可能吗?

【问题讨论】:

  • 嗯,似乎有点不一致,因为const fooObject: Foo = {a: "hello"}; fooObject.b = 1; 确实会引发编译器错误,但传播不会。
  • 是的,事实上const foo: Foo = { ...bar, b: 2 } 也会抱怨。不知道为什么。
  • 另外const foo: Foo = Object.assign({}, { ...barObject }, {somethingElse: true}) 不抱怨...
  • @Kousha 此外,编译器知道 barObject 的结构,因为它正确地将传播标记为安全,因为a 属性匹配。如果您执行const fooObject: Foo = { ...({x: 1, y: 2}) };,则会引发错误,因为没有a 属性。在这些情况下,它只是从不标记额外的属性。
  • 好的,找到了更多关于这个的问题:stackoverflow.com/questions/48788052/… stackoverflow.com/questions/44525777/… stackoverflow.com/questions/47789057/… 基本上,这似乎是一个长期存在的事情。要么接受它(毕竟,编译器会阻止你使用fooObject.b),要么编写一些只选择相关属性进行分配的辅助函数。

标签: typescript


【解决方案1】:

有趣的问题。根据this issue,展开运算符的结果是触发excess property checks


// barObject has more props than Foo, but spread does not trigger excess property checks
const fooObject: Foo = { ...barObject };

// b is explicit property, so checks kicks in here
const foo: Foo = { ...bar, b: 2 }

目前没有用于 TypeScript 的 exact types,但您可以创建一个简单的类型检查来强制执行严格的对象传播:

// returns T, if T and U match, else never
type ExactType<T, U> = T extends U ? U extends T ? T : never : never

const bar: Bar = { a: "a string", b: 1 };
const foo: Foo = { a: "foofoo" }

const fooMergeError: Foo = { ...bar as ExactType<typeof bar, Foo> }; // error
const fooMergeOK: Foo = { ...foo as ExactType<typeof foo, Foo> }; // OK

使用辅助函数,我们可以稍微减少冗余:

const enforceExactType = <E>() => <T>(t: ExactType<T, E>) => t

const fooMergeError2: Foo = { ...enforceExactType<Foo>()(bar) }; // error

Code sample

【讨论】:

  • 所以这并不简单。我宁愿没有检查,也不愿降低代码的可读性。遗憾的是目前没有标志..
  • 在大多数情况下,您可能想要对象传播以添加额外的属性。在项目基础上全面禁用此功能没有多大意义。我认为,在案例基础上会更有用,但如果您不喜欢上述代码的可读性,您也可以在展开操作之前强制执行对象的类型(每个变量或函数参数类型)。您还可以跟踪确切的类型问题(不知道,如果它得到实施)。
  • @ysfaran 一个补充:对象传播目前的行为与整个结构类型系统/鸭子类型一致,其中子类型可以分配给基本类型。在正常的变量赋值中,当您初始化一个新的对象文字时,您只会得到多余的属性检查。例如你可以写const fooObject: Foo = bar,因为bar不再是一个新的对象字面量,而是Foo的子类型。 const fooObject: Foo = { ...bar } 的行为类似,bar 不再“新鲜”/之前已经声明过。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多