【问题标题】:Dynamically modify/filter type to create a new type动态修改/过滤类型以创建新类型
【发布时间】:2021-01-13 09:15:26
【问题描述】:

给定一个类型,例如:

type PromiseFunc = () => Promise<unknown>;
type A = {
  key1: string;
  key2: string;
  key3: PromiseFunc;
  key4: string;
  key5: PromiseFunc;
  key6: SomeOtherType1[];
  key7: SomeOtherType2[];
  key8: string;
  key9: PromiseFunc;
  // ...
};

给定上述类型,我想创建一个新类型B,这样:

  • A 中所有 string 类型的键在 B 中都以 string 类型存在
  • A 中所有 PromiseFunc 类型的键在 B 中以 string 类型存在
  • A 中属于数组类型的所有键都从B 中排除

这可能以某种通用方式吗?我有数百种类型,例如A,但想找到一种方法将它们转换为B 之类的类型。我不控制A 类型的生成。

编辑: 链接到其他解决方案:https://www.typescriptlang.org/play?#code/C4TwDgpgBAIghsCAFATgewLYEsDO0C8UAFAJRT4B8UqmuEAPPIhQNwBQokUA4hAHYQUWAMY1sectXTiGOYEL4BzCmzYB6NVAAq4aMDRQAZlgA2iFB12wEcSQG82UKAGsIIAIwAuKHIWL2Tq4gAEzeTMjSdI4ubgDM3nB8IADaALrRQQAs3rwCQqKReBluAKzepORUYnT0fACuGABGgioAvuyWXACycGCouBhwOpBaaADKJmh8WM70APKNAFZQEAAeiHwAJjhQAEoQwmgom-S+WEoANFCJIBRXPcDCABYAanAmdRDDEFf7OHVmN4fL66KiEBxOZIAaSg5xiIDQhmoIlm0ScC0WFzRUAhTjx0LcsL48MRUAxqW8GIJIFSK3W-G2UAezyBn2+UAA-FAoYTvAIAG6CAJ41rJIKk8nRCgUvYQf6A95s3TsdqqTjQfqDap4UYTKYzb4AYSmgpQ5noWjBTN6-Rwg2+usm01mWiu4W1Pyg9SaLSgADJrX0hHahrpHfqXVdcoIRB6oAAfHzyc6KK5nJQqNiHPhyHyYCCsiDeTVwD3h51Gk2Cc1MOBW3FBLxQADkhjQaGbWMCblCUHcXfh2RbWE2mU7bHaQA

【问题讨论】:

标签: typescript


【解决方案1】:

好吧,我想出了如何做到这一点,我确信有一种更优化的方式来组合这些类型转换,但这是我目前拥有的并且它有效:

type PromiseFunc = () => Promise<unknown>;


// A type whose keys we want to drop.
type SomeOtherType1 = {
    key1: string[];
};

// Another type whose keys we want to drop.
type SomeOtherType2 = {
    key1: number;
};
type A = {
    key1: string;
    key2: string;
    key3: PromiseFunc;
    key4: string;
    key5: PromiseFunc;
    key6: SomeOtherType1[];
    key7: SomeOtherType2[];
    key8: string;
    key9: PromiseFunc;
};

// Start by building a filter type we can call with the types we want
// and converting all other types to a standard type.
type Filter<T, ConditionOne, ConditionTwo> = {
    [Key in keyof T]: T[Key] extends ConditionOne
        ? string
        : T[Key] extends ConditionTwo
        ? string
        : number;
};

// Do the conversion to a new type that only has string and number types.
type ConvertedType = Filter<A, string, PromiseFunc>;


// Now that we only have two types we can drop all the types that don't
// match the type we want (which is string), we'll convert all those keys
// to never which will drop them from our new type.
type PickByValueType<T, U> = {
    [K in keyof T as K extends K ? (T[K] extends U ? K : never) : never]: T[K];
};

// Generate the final type that is only the keys which are strings.
type FilteredType = PickByValueType<ConvertedType, string>;

我遇到的主要问题是我有number 我最初有never 我认为应该自动从类型中排除这些键,但它没有(它只是将类型更改为从不)。查看this issue on the TS issue tracker 提供了一些见解。显然这是在我正在使用的 TS 4.1.2 中,但我肯定还是做错了,如果Filter 类型与PickByValueType 类型结合使用,我相信这可以进一步优化。

对于以后可能遇到此答案的任何人,这里必须使用两种类型转换的原因是因为我们需要考虑和转换多种类型。如果你想简单地包含几种类型,你可以这样做:

...

type GrabTypes<T, U, U2> = {
    [K in keyof T as K extends K
        ? T[K] extends U
            ? K
            : T[K] extends U2
            ? K
            : never
        : never]: T[K];
};

type GrabbedTypes = GrabTypes<A, string, SomeOtherType1[]>

已经有几个关于使用omit 排除某些类型的问题,但我找不到很多好的例子可以让你删除不同类型的多个键。我认为这也可以以某种方式扩展以处理转换,但我想不出如何在三元运算符的约束下做到这一点:

...

type GrabTypesAndConvertOne<T, U, TypeToConvert> = {
    [K in keyof T as K extends K
        ? T[K] extends U
            ? K
            : T[K] extends TypeToConvert
            ? K // <- Here is where the type change logic would live
            : never
        : never]: T[K];
};

type GrabbedTypes = GrabTypesAndConvertOne<A, string, PromiseFunc>

【讨论】:

  • 感谢您的回答。我没有看到您在推荐中链接的那个问题,感谢您发现这一点。是的,关于要求#2,我遇到了同样的问题。我不控制类型A,但不幸的是,第二个要求对我正在尝试做的工作至关重要
  • @MatthewHerbst 我已经用满足所有标准的有效解决方案更新了我的答案,有机会时看看。也许你会想办法更​​好地优化它。如果你这样做,请标记我。
  • 谢谢!我在我的问题中添加了一个指向 typescriptlang.org 操场的链接,该链接有另一个解决方案,我想出了一个朋友(链接太长,无法放入此评论)
  • 感谢游乐场链接。这也是一个很好的解决方案。不幸的是,任何一个仍然感觉很乱。
猜你喜欢
  • 2011-12-21
  • 2023-01-09
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 2014-03-20
  • 1970-01-01
相关资源
最近更新 更多