我试图在打字稿2.8中找到新的conditional types。
例如,我有一些带有数组属性的对象,在我的流程中必须只有一个元素,我想得到这个值。 这是code I thought should work,它正确地只允许传递相关属性,但我无法弄清楚如何指定返回类型。我收到以下编译错误:
类型
number
不能用于索引类型Pick<T, { [K in keyof T]: T[K] extends any[] ? K : never; }[keyof T]>[K]
。
n
和s
的类型推断为number|string
而不是number
n
和string
s
}}
代码如下:
type ArrayProperties<T> = Pick<T, {
[K in keyof T]: T[K] extends Array<any>? K : never
}[keyof T]>;
const obj = {
a: 4,
n: [2],
s: ["plonk"]
};
// Compilation error on next line
function single<T, K extends keyof ArrayProperties<T>>(t: T, k: K): ArrayProperties<T>[K][number] {
const val = t[k];
if (!Array.isArray(val))
throw new Error(`Expected ${k} to be an array`);
if (val.length !== 1)
throw new Error(`Expected exactly one ${k}`);
return val[0];
}
const n = single(obj, "n"); // 'n' should be of type 'number'
const s = single(obj, "s"); // 's' should be of type 'string'
const a = single(obj, "a"); // Should fail to compile (OK)
答案 0 :(得分:3)
您可以使用一些变通方法来实现此功能。
要强制TypeScript编译器在无法验证属性存在时查找属性,可以将键类型与已知键相交。像这样:
type ForceLookup<T, K> = T[K & keyof T]; // no error
所以你可以改变
ArrayProperties<T>[K][number]
到
ForceLookup<ArrayProperties<T>[K],number>
让我们确保它有效:
type N = ForceLookup<ArrayProperties<typeof obj>["n"],number>; // number ✔️
type S = ForceLookup<ArrayProperties<typeof obj>["s"],number>; // string ✔️
n
和s
的较窄类型:问题是K
没有被推断为字符串文字。要提示编译器应尽可能为类型参数推断字符串文字,可以添加约束extends string
。它没有详细记录,但在某些特定情况下TypeScript infers literal types而不是扩展到更一般的类型(因此当1
被推断为1
而不是number
时,或'a'
被推断为'a'
而不是string
时)。约束keyof ArrayProperties<T>
显然不会触发此非扩展,因此K
在所有情况下都会扩展为keyof ArrayProperties<T>
。以下是K
的解决方法:
K extends string & keyof ArrayProperties<T>
让我们看看这一切:
declare function single<T, K extends string & keyof ArrayProperties<T>>(
t: T, k: K): ForceLookup<ArrayProperties<T>[K],number>;
const n = single(obj, "n"); // number ✔️
const s = single(obj, "s"); // string ✔️
const a = single(obj, "a"); // still error ✔️
全部完成!
嗯,我会在这里做一些简化。对于您实际可以使用的任何ArrayProperties<T>[K]
,T[K]
可以缩减为K
。所以你得到:
declare function single<T, K extends string & keyof ArrayProperties<T>>(
t: T, k: K): ForceLookup<T[K],number>;
现在一切都是真的。
希望有所帮助。祝你好运!