我正在使用查找类型,并希望构建一种安全合并的util函数(一个采用类型T
的实体和一个包含T
的键子集的对象更新)。我的目标是让编译器告诉我何时拼错一个属性或尝试为T
添加不存在的属性。
所以我有Person
并在(v2.1)中使用内置Partial
,如下所示:
interface Person {
name: string
age: number
active: boolean
}
function mergeAsNew<T>(a: T, b: Partial<T>): T {
return Object.assign({}, a, b);
}
现在我将其应用于以下数据:
let p: Person = {
name: 'john',
age: 33,
active: false
};
let newPropsOk = {
name: 'john doe',
active: true
};
let newPropsErr = {
fullname: 'john doe',
enabled: true
};
mergeAsNew(p, newPropsOk);
mergeAsNew(p, newPropsErr); // <----- I want tsc to yell at me here because of trying to assign non-existing props
我希望TS编译器在第二次调用时对我大喊大叫fullname
而enabled
不是Person
的道具。不幸的是,这在当地编译得很好,但是......当我在online TS Playground中做同样的事情时,我会得到或多或少的期望:
The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
Type argument candidate 'Person' is not a valid type argument because it is not a supertype of candidate '{ fullname: string; enabled: boolean; }'.
Property 'name' is missing in type '{ fullname: string; enabled: boolean; }'.
看起来这个游乐场使用与我在本地做同样的版本(2.1.4)。有没有人知道为什么这两者可能有所不同?
加分问题:
当我尝试以下任务时:
let x: Person = mergeAsNew(p, newPropsOk);
我在x
上收到以下错误,但仅限于操场上(本地一切都很好):
Type '{ name: string; active: boolean; }' is not assignable to type 'Person'.
Property 'age' is missing in type '{ name: string; active: boolean; }'.
为什么?不应该 属于Person
类型,因为第一个mergeAsNew
参数是 Person
而其他所有内容都是{{ 1}} - 道具子集(所以它最多Person
)?
修改
这是我的Person
:
tsconfig.json
答案 0 :(得分:1)
当你想表达一种类型只有另一种类型的属性子集时,普通的extends
也可以提供帮助
interface Person {
name: string
age: number
active: boolean
}
let p: Person = {
name: 'john',
age: 33,
active: false
};
let newPropsOk = {
name: 'john doe',
active: true
};
let newPropsErr = {
fullname: 'john doe',
enabled: true
};
// a extends b and not the other way round
//because we don't want to allow b to have properties not in a
function mergeAsNew<T2, T1 extends T2>(a: T1, b: T2): T1 {
return Object.assign({}, a, b);
}
mergeAsNew(p, newPropsOk); // ok
mergeAsNew(p, newPropsErr);
// Argument of type 'Person' is not assignable
// to parameter of type '{ fullname: string; enabled: boolean; }'.
// Property 'fullname' is missing in type 'Person'.
PS不知道在游乐场和地图类型上发生了什么
答案 1 :(得分:0)
好吧,看起来我昨天太固定了。我有一些睡眠,看起来(可能)不可能做我想要的那样(使用Partial
)。抛开这种奇怪的游乐场行为一段时间,这就是为什么(至少我是这么认为):
回顾一下:
interface Person {
name: string
age: number
active: boolean
}
function mergeAsNew<T>(a: T, b: Partial<T>): T {
return Object.assign({}, a, b);
}
在此代码中,Partial<Person>
可能(但不必)包含任何Person
道具,因此以下内容完全有效Partial<Person>
类型:
let newPropsErr = {
fullname: 'john doe',
enabled: true
};
由于Typescript具有结构类型,它不关心额外的道具,它只检查对象的形状,这里形状适合Partial<Person>
就好了。
以某种方式再次检查参数形状Partial<Person>
以获取额外道具的唯一方法是将其作为对象文字传递(严格检查文字以匹配类型)
现在mergeAsNew
功能本身。我在这里Object.assign
完全按照它的设计 - 将两个args的道具合并到新对象(所有这些)中。因为类型仅用于TS编译,所以在运行时无法限制并有选择地从b
中选择道具并将其应用于覆盖a
道具。
看起来@Artem's answer使用extend
执行我需要的操作而没有映射/查找类型。
无论如何,我仍然不明白为什么TS Playground以它的工作方式工作(最初给我的印象是所有这些东西都非常可行)。