打字稿:将可选与所需类型合并时,创建联合而不是交集 [英] Typescript: create union instead intersection when merging optional with required type

查看:48
本文介绍了打字稿:将可选与所需类型合并时,创建联合而不是交集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当可选属性和必需属性通过交集合并时,必需获胜

When optional and required property are merged via intersection, required wins

type A = { who: string }
type B = { who?: string }
// $ExpectType {who:string}
type R = A & B

例如在处理函数中的默认参数模式时,这可能会导致运行时错误

This may lead to runtime errors, when for instance, dealing with default params pattern within a function

type Params = {
  who: string
  greeting: string
}

const defaults: Params = {
  greeting: 'Hello',
  who: 'Johny 5',
}

function greeting(params: Partial<Params>){
  // $ExpectType Params
  const merged = {...defaults, ...params}

  return `${merged.greeting.toUpperCase()} ${merged.who} !`
}

// @throws - TypeError: Cannot read property 'toUpperCase' of undefined
greeting({greeting:undefined, who: 'Chuck'})

问题:

我所描述的是TS编译器的行为方式,问题是如何创建映射类型,该类型可以将交集解析为并集

as what I described is how TS compiler behaves, question is, how to create mapped type, that would resolve that intersection to union

类似:

type SafeMerge<A,B>=....

// $ExpectType {greeting?: string | undefined, who?: string | undefined }
type Result = SafeMerge<Params, Partial<Params>>

混合类型示例:

// $ExpectType {one?: number | undefined, two: string, three: boolean }
type Result = SafeMerge<{one: number, two:string}, {one?: number, three: boolean}>

推荐答案

获得一个合并类型,该合并类型是两种类型的合并,每种属性具有多种可能性.我们可以在两种组成类型的键上使用映射类型:

Getting a merged type that is an amalgamation of two types with each property a union of possibilities is simple. We can just use a mapped type, over the keys of both constituent types:

type SafeMerge<T, U> = {
  [P in keyof T |  keyof U] : 
    | (T extends Partial<Record<P, any>> ? T[P] : never)
    | (U extends Partial<Record<P, any>> ? U[P] : never)
}
type Result = SafeMerge<{one: number, two:string }, {one?: number, three: boolean }>
// Result: 
// type Result = {
//     one: number | undefined;
//     two: string;
//     three: boolean;
// }

播放

上述解决方案的问题是,我们失去了键的可选性(还有只读性,对此用例来说可能不太在意).同态映射类型保留修饰符,但不幸的是我们不能在此处使用修饰符,因为我们实际上并不适合同态映射类型的任何模式( {[P in keyof T]:T [P]} {[K中的P]:T [P]} ,其中K是带有 K的类型参数,扩展了T的key ,请参见

The problem with the solution above is that we loose the optionality of the keys (also the readonly-ness, which is probably less of a concern for this use case). Homomorphic mapped types keep modifiers, but unfortunately we can't use one here since we don't really fit in any of the patterns for homomorphic mapped types ({ [P in keyof T] : T[P] } or {[P in K]: T[P] } where K is a type parameter with K extends keyof T, see Here and Here).

我们可以提取可选键并使用两种映射类型,一种用于任何可选键(至少在组成部分中是可选键),另一种用于必需键:

We can extract the optional keys and use two mapped types, one for any optional keys (keys that are optional in at least one of the constituents) and one for the required keys:

type OptionalPropertyOf<T> = Exclude<{
  [K in keyof T]: T extends Record<K, T[K]>
    ? never
    : K
}[keyof T], undefined>

type SafeMerge<T, U> = {
  [P in OptionalPropertyOf<T> |  OptionalPropertyOf<U>]?: 
    | (T extends Partial<Record<P, any>> ? T[P] : never)
    | (U extends Partial<Record<P, any>> ? U[P] : never)
} & {
  [P in Exclude<keyof T | keyof U,  OptionalPropertyOf<T> |  OptionalPropertyOf<U>>]: 
    | (T extends Partial<Record<P, any>> ? T[P] : never)
    | (U extends Partial<Record<P, any>> ? U[P] : never)
}

type Result = SafeMerge<{one: number, two:string, three: number}, {one?: number, three: boolean}>
// Result: 
// type Result = {
//     one?: number | undefined;
// } & {
//     two: string;
//     three: number | boolean;
// }

type Id<T> = {} & { [P in keyof T]: T[P] }
type FlattenedResult = Id<SafeMerge<{one: number, two:string, three: number }, {one?: number, three: boolean}>>
// type FlattenedResult = {
//     one?: number | undefined;
//     two: string;
//     three: number | boolean;
// }

播放

可选地,我们可以像上面的示例中那样 Id 展平交点,但这是可选的.

Optionally we can Id to flatten the intersection as I did in the example above, but that is optional.

这篇关于打字稿:将可选与所需类型合并时,创建联合而不是交集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆