我想为将有效键与.
附加在一起的泛型函数采用无限个参数。从技术上讲,它可以使用与嵌套对象一样多的参数。
以下代码段按原样工作,但是我想知道它是否可以接受任意数量的参数,而不是仅手动输入的参数?
const tt = <Lang>() => {
return <
A extends keyof Lang,
B extends keyof Omit<Lang[A], keyof string>,
C extends keyof Omit<Lang[A][B], keyof string>,
D extends keyof Omit<Lang[A][B][C], keyof string>,
E extends keyof Omit<Lang[A][B][C][D], keyof string>,
F extends keyof Omit<Lang[A][B][C][D][E], keyof string>
// Can this repeat without manually typing each out?
>(
a: A,
b?: B,
c?: C,
d?: D,
e?: E,
f?: F,
) => {
return [a, b, c, d, e, f].filter(Boolean).join('.');
};
};
console.log(tt<{Parent: {child: 'child'}}>()('Parent', 'child'));
// Console: "Parent.child"
代码按原样运行,但是应该可以接受无限量的参数。
也许是这样的:
const tt = <Lang>() => {
return <A extends SomethingGoesHere>(...args: A) => {
return args.join('.');
};
};
console.log(tt<{Parent: {child: 'child'}}>()('Parent', 'child'));
// Console: "Parent.child"
一个后续问题:我讨厌这必须是一个高阶函数才能使通用类型起作用。为什么打字稿不允许我根据提供的通用类型A
来推断F
-Lang
?
在理想的世界中,我可以做到这一点:
const tt = <
Lang,
A extends keyof Lang,
B extends keyof Omit<Lang[A], keyof string>,
C extends keyof Omit<Lang[A][B], keyof string>,
D extends keyof Omit<Lang[A][B][C], keyof string>,
E extends keyof Omit<Lang[A][B][C][D], keyof string>,
F extends keyof Omit<Lang[A][B][C][D][E], keyof string>
>(
a: A,
b?: B,
c?: C,
d?: D,
e?: E,
f?: F,
) => {
return [a, b, c, d, e, f].filter(Boolean).join('.');
};
tt<{Parent: {child: 'child'}}>('Parent', 'child');
// ^ error: Expected 7 type arguments, but got 1.ts(2558)
但是它会抛出该错误。
答案 0 :(得分:1)
@ TitianCernicova-Dragomir是正确的,这里没有什么好(至少没有supported)。通常比使用TypeScript的类型系统更好的一件事是将代码重构为更好理解的内容。
例如,如果您不支持可变参数函数,而是创建一个可链接函数,其中每个调用将当前类型T
缩小为其属性T[K]
之一,该怎么办?像这样:
type ObjStrKey<T> = T extends object ? Extract<keyof T, string> : never;
interface Keychain<T> {
<K extends ObjStrKey<T>>(k: K): Keychain<T[K]>;
value: string;
}
function keychain<T>(): Keychain<T> {
function kc<T, K extends ObjStrKey<T>>(
this: Keychain<T>,
k: K
): Keychain<T[K]> {
const value = (this.value ? this.value + "." : "") + k;
const v = { value };
return Object.assign(kc.bind(v), v);
}
const v = { value: "" };
return Object.assign(kc.bind(v), v);
}
这是这样的:
interface Person {
parent: Person;
child: Person;
spouse: Person;
name: string;
}
const kcp = keychain<Person>();
console.log(kcp("child")("spouse")("child")("parent")("name").value);
// child.spouse.child.parent.name
对于编译器而言,这很容易,因为每个函数调用仅执行一次属性查找。
如果您发现对this
和bind
感到有些奇怪,可以将其更改为常规的class
并将函数调用更改为方法调用,如下所示:>
type ObjStrKey<T> = T extends object ? Extract<keyof T, string> : never;
class Keychain<T> {
static make<T>() {
return new Keychain<T>("");
}
private constructor(public value: string) {}
k<K extends ObjStrKey<T>>(k: K): Keychain<T[K]> {
const value = (this.value ? this.value + "." : "") + k;
return new Keychain<T[K]>(value);
}
}
有些不同:
const kcp = Keychain.make<Person>();
console.log(kcp.k("child").k("spouse").k("child").k("parent").k("name").value);
// child.spouse.child.parent.name
无论如何,只是一个想法。希望能帮助到你;祝你好运!