是否可以构造一个 TypeScript 类型,它只会选择那些 typeof X 的属性?
interface IA {
a: string;
b: number;
}
interface IB extends IA {
c: number | string;
}
type IAStrings = PickByType<IA, string>;
// IAStrings = { a: string; }
type IBStrings = PickByType<IB, string>;
// IBStrings = { a: string; }
type IBStringsAndNumbers = PickByType<IB, string | number>;
// IBStringsAndNumbers = { a: string; b: number; c: number | string; }
Run Code Online (Sandbox Code Playgroud)
小智 58
使用 Typescript 4.1,这可以变得更短,同时还允许选择可选属性,这是其他答案不允许的:
type PickByType<T, Value> = {
[P in keyof T as T[P] extends Value | undefined ? P : never]: T[P]
}
Run Code Online (Sandbox Code Playgroud)
作为解释这里发生的事情,因为这可能会被认为是黑魔法:
P in keyof TT存储所有可能的键Pas的用途PT[P]T[P]匹配Value | undefined(undefined以允许可选属性)。T[P]匹配Value | undefined,则我们将其设置P为该类型的属性及其对应的值T[P]never最终不会出现在结果类型中,从而显式删除与您要选择的类型不匹配的任何属性。hlo*_*dal 13
是的,这是可能的。我来到这个问题也是为了寻找答案,但我最终想通了。
TL; 博士
/**
* Returns an interface stripped of all keys that don't resolve to U, defaulting
* to a non-strict comparison of T[key] extends U. Setting B to true performs
* a strict type comparison of T[key] extends U & U extends T[key]
*/
type KeysOfType<T, U, B = false> = {
[P in keyof T]: B extends true
? T[P] extends U
? (U extends T[P]
? P
: never)
: never
: T[P] extends U
? P
: never;
}[keyof T];
type PickByType<T, U, B = false> = Pick<T, KeysOfType<T, U, B>>;
Run Code Online (Sandbox Code Playgroud)
更长的解释性版本
class c1 {
a: number;
b: string;
c: Date;
d?: Date;
};
type t1 = keyof c1; // 'a' | 'b' | 'c' | 'd'
type t2 = Pick<c1, t1>; // { a: number; b: string; c: Date; d?: Date; }
type KeysOfType0<T, U> = {
[P in keyof T]: T[P] extends U ? P : never;
};
type t3 = KeysOfType0<c1, Date>; // { a: never; b: never; c: "c"; d?: "d"; }
// Based on https://github.com/microsoft/TypeScript/issues/16350#issuecomment-397374468
type KeysOfType<T, U> = {
[P in keyof T]: T[P] extends U ? P : never;
}[keyof T];
type t4 = KeysOfType<c1, Date>; // "c" | "d"
type t5 = Pick<c1, t4>; // { c: Date; d?: Date; }
type PickByType<T, U> = Pick<T, KeysOfType<T, U>>;
type t6 = PickByType<c1, Date>; // { c: Date; d?: Date; }
Run Code Online (Sandbox Code Playgroud)
因此,这PickByType正是您在评论中获得的结果。
如果您需要一个严格类型的实用程序,您需要验证扩展是双向的。下面是原始 KeysOfType 实用程序可能返回意外结果的一种情况和两种解决方案的示例。
在打字稿操场上试一试。
type KeysOfType<T, U> = {
[P in keyof T]: T[P] extends U ? P : never;
}[keyof T];
type PickByType<T, U> = Pick<T, KeysOfType<T, U>>;
type KeysOfTypeStrict<T, U> = {
[P in keyof T]: T[P] extends U ? (U extends T[P] ? P : never) : never;
}[keyof T];
type PickByTypeStrict<T, U> = Pick<T, KeysOfTypeStrict<T, U>>;
/**
* Returns an interface stripped of all keys that don't resolve to U, defaulting
* to a non-strict comparison of T[key] extends U. Setting B to true performs
* a strict type comparison of T[key] extends U & U extends T[key]
*/
type KeysOfTypeBest<T, U, B = false> = {
[P in keyof T]: B extends true
? T[P] extends U
? (U extends T[P]
? P
: never)
: never
: T[P] extends U
? P
: never;
}[keyof T];
type PickByTypeBest<T, U, B = false> = Pick<T, KeysOfTypeBest<T, U, B>>;
interface thing {
foo: () => string;
bar: (resourceName: string) => string;
test: string;
}
type origBar = PickByType<thing, thing['bar']>;
let origBar: Partial<origBar> = {};
origBar.bar; // success: true positive
origBar.foo; // success: false positive, I wasn't expecting this property to be allowed.
origBar.test // error: true negative
type origFoo = PickByType<thing, thing['foo']>;
let origFoo: Partial<origFoo> = {};
origFoo.bar; // error: true negative
origFoo.foo; // success: true positive
origFoo.test // error: true negative
type strictBar = PickByTypeStrict<thing, thing['bar']>;
let strictBar: Partial<strictBar> = {};
strictBar.bar; // success: true positive
strictBar.foo; // error: true negative
strictBar.test // error: true negative
type strictFoo = PickByTypeStrict<thing, thing['foo']>;
let strictFoo: Partial<strictFoo> = {};
strictFoo.bar; // error: true negative
strictFoo.foo; // sucess: true positive
strictFoo.test // error: true negative
type bestBarNonStrict = PickByTypeBest<thing, thing['bar']>;
let bestBarNonStrict: Partial<bestBarNonStrict> = {};
bestBarNonStrict.bar; // success: true positive
bestBarNonStrict.foo; // success: true positive, I do want to keep properties with values similar to bar
bestBarNonStrict.test // error: true negative
type bestBarStrict = PickByTypeBest<thing, thing['bar'], true>;
let bestBarStrict: Partial<bestBarStrict> = {};
bestBarStrict.bar; // success: true negative
bestBarStrict.foo; // error: true negative, I do NOT want to keep properties with values similar to bar
bestBarStrict.test // error: true negative
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3235 次 |
| 最近记录: |