您可以采取的一种方法是将constraint也添加到T
,这样就可以有效地禁止除Q
或M
之外的任何键的属性(使用the impossible never
type:
function createService<
T extends Record<Exclude<keyof T, Q | M>, never>,
Q extends keyof T,
M extends Exclude<keyof T, Q>
>(obj: T, queries: readonly Q[], mutations: readonly M[]) { }
interface Test {
a: () => void;
b: () => void;
}
const obj: Test = { a: () => { }, b: () => { } };
createService(obj, ["a"], ["b"]); // OK
createService(obj, ["a", "b"], []); // OK
createService(obj, ["a", "b"], ["b"]); // Error
createService(obj, ["a"], []); // Error
createService(obj, [], ["b"]); // Error
现在,这起到了预期的作用.
请注意,这并没有考虑到所有可能的情况.TypeScrip本身并不代表exhaustive arrays的概念.类型为(T1 | T2 | T3)[]
的数组不一定都有类型为T1
、T2
和T3
的元素.它根本不能保证有任何元素.所以有could个人这样做:
const aArr: "a"[] = [];
const bArr: "b"[] = [];
createService(obj, aArr, bArr);
想必这不是您关心的用例.如果是,那么唯一可能的方法是列举每一种可能的可接受的tuple输入:
type AllPossiblePartialTuples<T, U = T> =
[T] extends [never] ? readonly [] : T extends any ?
readonly [T, ...AllPossiblePartialTuples<Exclude<U, T>>] |
AllPossiblePartialTuples<Exclude<U, T>>
: never;
type AllPossibleTuples<T, U = T> =
[T] extends [never] ? readonly [] : T extends any ?
readonly [T, ...AllPossibleTuples<Exclude<U, T>>] : never;
declare function createService<
T extends object,
const Q extends AllPossiblePartialTuples<keyof T>
>(
obj: T,
queries: Q,
mutations: AllPossibleTuples<
Exclude<keyof T, Q extends readonly (infer V)[] ? V : never>
>
): void;
const obj: Test = { a: () => { }, b: () => { } };
createService(obj, ["a"], ["b"]); // OK
createService(obj, ["a", "b"], []); // OK
createService(obj, ["a", "b"], ["b"]); // Error
createService(obj, ["a"], []); // Error
createService(obj, [], ["b"]); // Error
但只有在T
只有少量密钥的情况下,这才是可信的.即使是数量不多的键,这些键的所有可能排列的并集也非常大,并且会导致性能问题或关于复杂性限制的编译器错误,或者两者兼而有之:
// okay-ish
createService(
{ a: 0, b: 1, c: 2, d: 3, e: 4, f: 5 },
['a', 'c', 'e'],
['b', 'd', 'f']
);
// 🔥💻🔥 COMPILER EATS UP CPU RESOURCES
createService(
{ a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h:7},
['a', 'c', 'e', 'g'],
['b', 'd', 'f', 'h']
);
// ☠🔥💻🔥☠ EVENTUALLY ISSUES AN ERROR ABOUT COMPLEXITY
createService(
{ a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h:7, i: 8, j: 9, k: 10},
['a', 'c', 'e', 'g', 'i', 'k'],
['b', 'd', 'f', 'h', 'j']
);
因此,我可能建议您使用第一种方法,除非您有非常特殊的用例.
Playground link to code个