在下面的代码(游乐场)中,我试图使autocompletion和函数签名推理同时在personTypeX({...}) 中工作,但只能实现这两种功能之一,但不能同时实现两者。
有可能吗?
以下是要求:
firstName,那么它必须是一个string。age,那么它必须是一个number。firstName、lastName、age、favouriteFood或getLegCount,那么该属性的值本身必须是Person。总共有3项额外所需资源
personType1()和personType2()一起工作firstName中所有键的自动完成,.,getLegCount必须在personTypeX({})中提供--参见下面的内容:当前只在personType2({})中工作getLegCount)强制其值为函数,则必须正确推断函数签名如下:当前仅在personType1({})中工作。type FixedType = {
firstName: string,
lastName: string,
age: number,
favouriteFood: string[],
getLegCount: (numHeads: number) => number,
};
type PersonType<T> = {
[K in (keyof T | keyof FixedType)]?:
K extends keyof FixedType
? FixedType[K]
: K extends keyof T
? T[K] extends object
? PersonType<T[K]>
: PersonType<{}>
: never
}
// ---- ---- ---- ---- ----
function personType1<T extends PersonType<T>>(t: T) { // signature version 1
return t;
}
const person11 = personType1({
firstName: 'Mark',
lastName: 'Antony',
holger: {
age: 12,
paul: {
// <-- type 'a' for autocompletion of 'age' => NOT WORKING
favouriteFood: ['cheese'],
firstName: 'Paul',
getLegCount: (numHeads) => 2, // <-- hover over 'numHeads' => numHeads: number correctly inferred => WORKING
}
},
});
// ----
function personType2<T extends PersonType<T>>(t: T & PersonType<T>) { // signature version 2
return t;
}
const person1 = personType2({
firstName: 'Mark',
lastName: 'Antony',
holger: {
age: 12,
paul: {
// <-- type 'a' for autocompletion of 'age' => WORKING
favouriteFood: ['cheese'],
firstName: 'Paul',
getLegCount: (numHeads) => 2, // <-- hover over 'numHeads' => numHeads: any inferred => NOT WORKING
// that's probably bc it's the most common signature for
// (property) getLegCount: ((numHeads: any) => number) & ((numHeads: number) => number)
// but I have no clue how to fix this
}
},
});发布于 2022-06-14 23:17:38
简而言之,你现在所要求的是不可能的(就我的理解而言)。
这里有很多循环逻辑,它是TS的一个限制(在管理/理解这类代码方面是对人类的冒犯)。实际上,我们在泛型中使用泛型来规避它,但我们仍然受到它的限制。也就是说,根据我的理解,我们只能选择一个:
getLegCount: (numHeads) => 2),使参数的数据类型(IE )。关于非显式类型的参数推断()这方面的一个例子是,当我们回到泛型是如何制作的时候。
function example<T>(t: T)在这里,我们可以显式定义T以规定t遵循,也可以从t推断T
当我们这样做的时候:
function personType<T extends PersonTypeNew<T>>(t: T){...}我们正在制作循环逻辑,在某些时候TS必须选择T或t。(尽管他们彼此依赖,但他们创造了不同的结果)。
getLegCount: (numHeads),则推断age为空(因为它不在参数中),并且不需要为age (或任何其他键)提供自动完成功能。从技术上讲,这是一个超级超级简化。这实际上在简单类型对象(即。(非循环的)带有称为部分推理的东西,这将允许自动完成和参数推断。
如果这让人困惑的话,在操场上有一些例子。
不管怎样,我把它从头开始重写了。支持自动完成和参数推断,但只有前者不存在。
type PersonTypeNew<T> = {
// We have to overwrite any keys, otherwise we could incorrectly infer.
// Hence Omit<T, keyof FixedType>, we use & Partial<FixedType> for correct autocompletion
[K in keyof (Omit<T, keyof FixedType> & Partial<FixedType>)]:
K extends keyof FixedType
? FixedType[K]
: (Omit<T, keyof FixedType> & Partial<FixedType>)[K] extends Record<string, any>
? PersonTypeNew<(Omit<T, keyof FixedType> & Partial<FixedType>)[K]>
: "Invalid Value!"
}这也增加了第一层的自动完成(当你没有推断)。
在没有自动完成支持的情况下,它要简洁得多(因为我们只是验证而不交叉两种对象类型)
type PersonTypeNewNoAutocomplete<T> = {
[K in keyof T]?:
K extends keyof FixedType
? T[K] extends FixedType[K]
? T[K]
: FixedType[K]
: PersonTypeNewNoAutocomplete<T[K]>
}还有一些尚未解决的问题正在讨论这一问题,本文将进一步详细介绍上下文引擎、优先级、循环类型的其他相关主题:
T extends M,这将讨论自动完成如何不工作发布于 2022-06-20 16:53:39
检查一下,它的工作方式是使用Partial<FixedType>进行自动完成,使用K extends keyof FixedType ? FixedType[K]检查错误,并为嵌套对象使用递归的泛型类型。
代码:
type TypeCheckedInferedPerson<T> = Partial<FixedType> & PersonType<T>
type PersonType<T extends Partial<FixedType>> = {
[K in keyof T]: K extends keyof FixedType ? FixedType[K] : TypeCheckedInferedPerson<T[K]>
}
function personType2<T extends PersonType<T>>(t: TypeCheckedInferedPerson<T>) {
return t;
}结果:

https://stackoverflow.com/questions/72593943
复制相似问题