仅接受数组值键的函数(并推导 return 类型)
Function that accepts only keys of array values (and deduces return type)
我正在尝试理解打字稿 2.8 中的新 conditional types。
例如,我有一些具有数组属性的对象,在我的流中必须只有一个元素,我想获得这个值。
这是 code I thought should work,它正确地只允许传入相关属性,但我不知道如何指定 return 类型。我收到以下编译错误:
Type number
cannot be used to index type Pick<T, { [K in keyof T]: T[K] extends any[] ? K : never; }[keyof T]>[K]
.
并且 n
和 s
的类型被推断为 number|string
而不是 n
的 number
和 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)
您可以使用一些解决方法来实现此目的。
修复报告的错误:
要强制 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
。它不是 well-documented,但在某些特定情况下 TypeScript infers literal types 而不是扩大到更一般的类型(因此当 1
被推断为 1
而不是 number
,或者当 'a'
被推断为 'a'
而不是 string
时)。约束 keyof ArrayProperties<T>
显然不会触发此 non-widening,因此在所有情况下 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>;
现在全部完成。
希望对您有所帮助。祝你好运!
我正在尝试理解打字稿 2.8 中的新 conditional types。
例如,我有一些具有数组属性的对象,在我的流中必须只有一个元素,我想获得这个值。 这是 code I thought should work,它正确地只允许传入相关属性,但我不知道如何指定 return 类型。我收到以下编译错误:
Type
number
cannot be used to index typePick<T, { [K in keyof T]: T[K] extends any[] ? K : never; }[keyof T]>[K]
.
并且 n
和 s
的类型被推断为 number|string
而不是 n
的 number
和 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)
您可以使用一些解决方法来实现此目的。
修复报告的错误:
要强制 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
。它不是 well-documented,但在某些特定情况下 TypeScript infers literal types 而不是扩大到更一般的类型(因此当 1
被推断为 1
而不是 number
,或者当 'a'
被推断为 'a'
而不是 string
时)。约束 keyof ArrayProperties<T>
显然不会触发此 non-widening,因此在所有情况下 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>;
现在全部完成。
希望对您有所帮助。祝你好运!