【问题标题】:Remove null and undefined from type (including nested props)从类型中删除 null 和 undefined(包括嵌套道具)
【发布时间】:2022-10-13 08:18:38
【问题描述】:

我找到了这个资源,它非常适合没有嵌套道具的类型。 https://bobbyhadz.com/blog/typescript-remove-null-and-undefined-from-type

但就我而言,我需要剥离所有道具,甚至是嵌套的道具。

有什么解决办法吗?

笔记。我的类型会自动生成数百个,因此手动执行它不是一种选择。

示例类型:

type BlogSlugQuery = {
    __typename?: "Query" | undefined;
    Blogs?: {
        __typename?: "Blogs" | undefined;
        docs?: ({
            __typename?: "Blog" | undefined;
            slug?: string | null | undefined;
        } | null)[] | null | undefined;
    } | null | undefined;
}

【问题讨论】:

    标签: typescript graphql-codegen


    【解决方案1】:

    这似乎有效:

    type WithoutNullableKeys<Type> = {
      [Key in keyof Type]-?: Type[Key] extends (object | undefined | null) 
         ? WithoutNullableKeys<Type[Key]> 
         : NonNullable<Type[Key]>;
    };
    

    尽管显示的类型表达式如下:

    type T2 = {
        __typename: "Query";
        Blogs: WithoutNullableKeys<{
            __typename?: "Blogs" | undefined;
            docs?: ({
                __typename?: "Blog" | undefined;
                slug?: string | null | undefined;
            } | null)[] | null | undefined;
        }> | null;
    }
    

    ...因此递归不会被取消,它似乎正确地警告省略属性和/或将它们设置为空/未定义:

    TS Playground

    【讨论】:

    • 不知道应该如何处理空数组 - 是否应该允许。当前类型定义传递一个空数组。
    【解决方案2】:

    ? 我想出了一种方法来做到这一点,结果的用法对我来说非常易读。我将another Stack Overflow answer 扩展为jcalz

    类型

    const NotNullSymbol = Symbol("not null");
    export type NotNull = typeof NotNullSymbol;
    
    type RemoveNotNullTypes<T> = T extends NotNull
      ? unknown
      : T extends object
      ? { [K in keyof T]: RemoveNotNullTypes<T[K]> }
      : T;
    
    type _Overwrite<T, U> = U extends NotNull
      ? Exclude<T, null>
      : U extends object
      ? {
          [K in keyof T]: K extends keyof U ? _Overwrite<T[K], U[K]> : T[K];
        } & RemoveNotNullTypes<U>
      : U;
    
    type ExpandRecursively<T> = T extends Function
      ? T
      : T extends object
      ? T extends infer O
        ? { [K in keyof O]: ExpandRecursively<O[K]> }
        : never
      : T;
    
    export type Overwrite<T, U> = ExpandRecursively<_Overwrite<T, U>>;
    

    示例用法

    type Person = {
      name: string | null;
      house: {
        kitchen: {
          stoveName: string | null;
          stoveBrand: number | undefined;
          otherThings: unknown;
        };
      };
    };
    
    type PersonWithNullsRemoved = Overwrite<
      Person,
      {
        name: NotNull;
        house: {
          kitchen: {
            stoveName: NotNull;
            stoveBrand: string;
          };
        };
      }
    >;
    
    function foo(person: PersonWithNullsRemoved) {
      // no TS errors for the following lines
      const name = person.name.toLowerCase();
      const stoveName = person.house.kitchen.stoveName.toLowerCase();
      const stoveBrand = person.house.kitchen.stoveBrand.toLowerCase();
    }
    function bar(person: Person) {
      const name = person.name.toLowerCase(); // Error: Object is possibly 'null'
      const stoveName = person.house.kitchen.stoveName.toLowerCase(); // Error: Object is possibly 'null'
      const stoveBrand = person.house.kitchen.stoveBrand.toLowerCase(); // Error: Object is possibly 'undefined' and Error: Property 'toLowerCase' does not exist on 'number'.
    }
    

    解释

    我不会深入探讨Overwrite 的一般工作原理,因为这已经在the SO answer I was inspired by 中完成。我用NotNull 类型扩展了它,以避免必须覆盖像这样的深层嵌套属性:Exclude&lt;Person['house']['kitchen']['stoveName'], null&gt;,当它更加嵌套时会变得非常忙碌。相反,简单的NotNull 对我来说更好读!

    NotNull 只是特定 unique symbol 的类型。或者,一个唯一的字符串 const 可能就足够了,但可能会导致意外匹配。

    _Overwrite 评估传入的覆盖映射时,如果值为NotNull,那么它将只取原始类型的值并从中排除null。否则,如果它是一个对象,它将遵循正常路径。但是,在将对象与U 合并时,我们需要确保NotNull 类型不会以最终发出的类型结束。所以我们 RemoveNotNullTypes 来自 U 和任何 Us 嵌套属性。

    这个实现在生产环境中运行良好,我通过删除nulls 来覆盖Prisma 返回的对象的类型,其中业务逻辑在给定情况下不允许null。有时你可以只添加! 来声明你不希望它是null 但在这种情况下,我们试图让发出的类型与生成的ResponseBody 类型匹配ResponseBody 类型@ 类型.

    如果某些事情仍然没有意义,请告诉我,我很乐意尝试进一步解释。

    【讨论】:

      猜你喜欢
      • 2019-04-02
      • 1970-01-01
      • 2019-08-11
      • 2021-12-24
      • 2023-02-06
      • 1970-01-01
      • 2011-11-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多