asn*_*aeb 4 typescript typescript-generics
我只需要选择两个名称尚未从类型中定义的属性,然后从那里创建一个新类型,其中一个属性是必需的,另一个属性是可选的。
我知道可以选择一个单一的房产
<T extends Record<string,any>> {
[K in keyof T]: (Record<K, T[K]> &
Partial<Record<Exclude<keyof T, K>, never>>) extends infer U ? { [P in keyof U]: U[P] } : never
}[keyof T]
Run Code Online (Sandbox Code Playgroud)
但没有弄清楚如何(以及是否)可以使用此方法选择两个属性。
下面是我想如何使用它的示例
class Article {
name: string
id: number
content?: string
}
const article: TwoKeys<Article> = { id: 23 } // no error
const article: TwoKeys<Article> = { name: "my article", id: 122 } // no error
const article: TwoKeys<Article> = { name: "my article" , id: 23, content: "my content" } // error! we passed more than two props.
Run Code Online (Sandbox Code Playgroud)
首先,让我们创建一个名为 的辅助类型,PickOnly<T, K>您可以在其中获取类对象类型T和键类型K(或此类键的并集),并生成一个新的类对象类型,其中已知存在Twith 键的属性(就像实用程序类型) 中的键已知不存在( 中 中不需要):KPick<T, K>TPick<T, K>
type PickOnly<T, K extends keyof T> =
Pick<T, K> & { [P in Exclude<keyof T, K>]?: never };
Run Code Online (Sandbox Code Playgroud)
该实现与禁止除 中的键以外的键的类型相交 。该类型使用实用程序类型来获取非键,并表示它们必须都是可选属性,其值类型是不可能类型。可选属性可能会丢失(或取决于编译器选项),但属性不能存在......这意味着这些属性必须始终丢失(或)。Pick<T, K>TK{[P in Exclude<keyof T, K>]?: never}Exclude<T, U>KTneverundefinedneverundefined
一个例子:
let x: PickOnly<{a: string, b: number, c: boolean}, "a" | "c">;
x = {a: "", c: true} // okay
x = {a: "", b: 123, c: true} // error!
// -------> ~
//Type 'number' is not assignable to type 'never'.
x = {a: ""}; // error! Property 'c' is missing
Run Code Online (Sandbox Code Playgroud)
type 的值X必须是 an {a: number, c: boolean},而且根本不能包含b属性。
因此,您想要的可能是每个可能的键集(其中最多有两个元素)组成AtMostTwoKeys<T>的并集。因为看起来像PickOnly<T, K>KTArticle
| PickOnly<Article, never> // no keys
| PickOnly<Article, "name"> // only name
| PickOnly<Article, "id"> // only id
| PickOnly<Article, "content"> // only content
| PickOnly<Article, "name" | "id"> // name and id
| PickOnly<Article, "name" | "content"> // name and content
| PickOnly<Article, "id" | "content"> // id and content
Run Code Online (Sandbox Code Playgroud)
那么让我们构建AtMostTwoKeys<T>. 没有键的部分很简单:
type AtMostTwoKeys<T> = (
PickOnly<T, never> |
)'
Run Code Online (Sandbox Code Playgroud)
现在对于一个关键...最简单的方法是通过microsoft/TypeScript#47109中创造的形式的分布式对象类型。形式的类型,您可以立即索引到映射类型,并生成for all in the union的并集。{[K in KK]: F<K>}[KK]F<K>KKK
因此对于一把钥匙来说,看起来像:
type AtMostTwoKeys<T> = (
PickOnly<T, never> |
{ [K in keyof T]: PickOnly<T, K> }[keyof T]
);
Run Code Online (Sandbox Code Playgroud)
哦,但是in keyof T使映射类型同态,这可能会undefined在可选输入属性的输出中引入不需要的值,我将抢先使用-?映射类型修饰符从映射中删除可选修饰符:
type AtMostTwoKeys<T> = (
PickOnly<T, never> |
{ [K in keyof T]-?: PickOnly<T, K> }[keyof T]
);
Run Code Online (Sandbox Code Playgroud)
对于两个键来说,事情有点棘手。我们想要在这里做两层分布式对象。第一个迭代K中的每个键keyof T,第二个应该引入一个新的类型参数(例如L)来执行相同的操作。然后K | L将是来自 的每对可能的键keyof T,以及每个单个键(当K和L相同时)。这会重复计算不同的对,但这不会造成任何伤害:
type AtMostTwoKeys<T> = (
PickOnly<T, never> |
{ [K in keyof T]-?: PickOnly<T, K> |
{ [L in keyof T]-?:
PickOnly<T, K | L> }[keyof T]
}[keyof T]
)
Run Code Online (Sandbox Code Playgroud)
基本上就是这样,但结果类型将用以下形式表示PickOnly:
type AMTKA = AtMostTwoKeys<Article>;
/* type AMTKA = PickOnly<Article, never> | PickOnly<Article, "name"> |
PickOnly<Article, "name" | "id"> | PickOnly<Article, "name" | "content"> |
PickOnly<Article, "id"> | PickOnly<Article, "id" | "content"> | \
PickOnly<Article, "content"> */
Run Code Online (Sandbox Code Playgroud)
也许这样就可以了。但通常我喜欢引入一个小助手来将这些类型扩展到它们的实际属性:
type AtMostTwoKeys<T> = (
PickOnly<T, never> |
{ [K in keyof T]-?: PickOnly<T, K> |
{ [L in keyof T]-?:
PickOnly<T, K | L> }[keyof T]
}[keyof T]
) extends infer O ? { [P in keyof O]: O[P] } : never
Run Code Online (Sandbox Code Playgroud)
让我们再试一次:
type AMTKA = AtMostTwoKeys<Article>;
/* type AMTKA =
| { name?: never; id?: never; content?: never; } // no keys
| { name: string; id?: never; content?: never; } // only name
| { name: string; id: number; content?: never; } // name and id
| { name: string; content?: string; id?: never; } // name and content
| { id: number; name?: never; content?: never; } // only id
| { id: number; content?: string; name?: never; } // id and content
| { content?: string; name?: never; id?: never; } // only content
*/
Run Code Online (Sandbox Code Playgroud)
看起来不错!
为了确定起见,让我们检查一下您的示例用例:
let article: AtMostTwoKeys<Article>;
article = { id: 23 } // okay
article = { name: "my article", id: 122 } // okay
article = { name: "my article", id: 23, content: "my content" } // error!
Run Code Online (Sandbox Code Playgroud)
成功!
| 归档时间: |
|
| 查看次数: |
5567 次 |
| 最近记录: |