typescript 类型脚本:将可选属性转换为必需的不可为空的属性(某些属性除外)

8yoxcaq7  于 2022-12-19  发布在  TypeScript
关注(0)|答案(1)|浏览(269)

我希望将具有可选属性的对象转换为必需的可为null的属性,同时保持必需的属性不变。如果属性的类型为NotNullable,则在转换中不应为null。
例如:

type A {
   a: number;
   b?: NotNullable<boolean>;
   c?: boolean;
}

调用OptionalToNullable实用程序类型应提供以下内容:

type B = OptionalToNullable<A>
/* 
   {
      a: number;
      b: boolean;
      c: boolean | null;
   }
*/

我试过:

export type NotNullable<T> = T extends null ? never : T;

export type OptionalToNullable<O> = {
    [K in keyof O]-?: NotNullable<O[K]> extends NotNullable<infer V>
        ? V
        : undefined extends O[K]
        ? NonNullable<O[K]> | null
        : O[K];
};

但不管用有什么建议吗

8cdiaqws

8cdiaqws1#

// use unique symbol to prevent collision
declare const unique: unique symbol

// use this type to "mark"(by union) a key so you can differentiate it and run extra operation on it
// in this case, key with this type will not union with null
type CannotBeNull = typeof unique

// find optional key by comparing X to Required<X>
type FindOptionalKey<T extends Record<string, unknown>> = Exclude<{ [K in keyof T]: T[K] extends Required<T>[K] ? never : K }[keyof T], undefined>

// Remove the optional key, we are going to rebuild them later
type OmitOptionalKey<T extends Record<string, unknown>> = Omit<T, FindOptionalKey<T>>

// Remove CannotBeNull after it serves its purpose
// also remove undefined type
type RemoveSpecialType<T> = Exclude<T, CannotBeNull | undefined>

// check whether the key is marked with CannotBeNull
// if so do not union with null
type IsUnionWithSpecialType<T> = Extract<T, CannotBeNull> extends never ? T | null: T

// Rebuild the omitted optional key
type RebuildOptionalKey<T extends Record<string, unknown>> = { [K in FindOptionalKey<T>]: RemoveSpecialType<IsUnionWithSpecialType<T[K]>> }

// just to make the type look nicer in hint, does not change the result or what so ever
// without it, the type look like OmitOptional<A> & { b:string, c: boolean | null } instead of  {a: number, b:string, c: boolean | null } 
type Prettify<T extends Record<string, unknown>> = { [K in keyof T]: T[K] }

// finally combine all the keys
type ConvertOptionalToNullUnion<T extends Record<string, unknown>> = Prettify<OmitOptionalKey<T> & RebuildOptionalKey<T>>

type A = {
   a: number;
   b?: string | CannotBeNull;
   c?: boolean;
}

type D = ConvertOptionalToNullUnion<A>

运动场

相关问题