简体   繁体   English

多个嵌套泛型的功能组合的打字稿等价物

[英]Typescript equivalent for functional compose for multiple nested generics

Sometimes I do a lot of operations on a type in order to create a new one, ie:有时我对一种类型进行大量操作以创建一个新类型,即:

type Complex = Omit<Merge<Interface1, {modifiedField: false}>, 'omitted field'>

Is there any equivalent of a functional compose or pipe functions so that creating such a type would more readable, ie:是否有功能composepipe功能的等价物,以便创建这样的类型更具可读性,即:

type Complex = Pipe<Interface1, 
   Merge<{modified: false}>,
   Omit<'omitted field'>
>

No, the language doesn't have higher-order type manipulation of the kind needed for this to work.不,该语言没有运行所需类型的高阶类型操作。 The closest I can imagine is to drop from the type level to the value level, abuse the recent support for higher order function inference from generic functions , and then move back up to the type level.我能想象的最接近的是从类型级别下降到值级别,滥用最近对泛型函数的高阶函数推断的支持,然后又回到类型级别。 It's not pretty and you end up with a little useless runtime code to boot.它并不漂亮,并且您最终会得到一些无用的运行时代码来启动。

Here's what I mean.这就是我的意思。 I'm assuming you have these definitions:我假设你有这些定义:

interface Interface1 {
    a: string;
    "omitted field": boolean;
}
type Merge<T, U> = T & U;
type ManualComplex = Omit<Merge<Interface1, { modifiedField: false }>, 'omitted field'>
/* type ManualComplex = {
    a: string;
    modifiedField: false;
} */

Then here's the hoop-jumping with values I'm talking about:然后这是我正在谈论的价值的跳圈:

declare const CurriedOmit: <K extends PropertyKey>() => <T>(t: T) => Omit<T, K>;
declare const CurriedMerge: <U>() => <T>(t: T) => Merge<T, U>;
declare const Id: <T>() => T

I'm pretending that there's a curried Omit and Merge , as well as an Id .我假装有一个咖喱OmitMerge ,以及一个Id Then we can pretend that there's a pipe() function that applies these in order:然后我们可以假设有一个pipe()函数按顺序应用这些:

// there may be some variadic pipe but for now let's do this:
declare const pipe: {
    <A>(a: A): A;
    <A, B>(a: A, b: (a: A) => B): B;
    <A, B, C>(a: A, b: (a: A) => B, c: (b: B) => C): C;
    <A, B, C, D>(a: A, b: (a: A) => B, c: (b: B) => C, d: (c: C) => D): D;
}

Finally I write some actual runtime code that fools the compiler into evaluating the type you want, although at runtime the code will short-circuit to true (which is good because if it were to actually call the non-existent pipe() function you'd get some yucky runtime error):最后,我编写了一些实际的运行时代码来欺骗编译器来评估您想要的类型,尽管在运行时代码将短路为true (这很好,因为如果它实际上调用不存在的pipe()函数,您d 得到一些令人讨厌的运行时错误):

const complex = (true as false) || pipe(
    Id<Interface1>(),
    CurriedMerge<{ modifiedField: false }>(),
    CurriedOmit<'omitted field'>()
);

Now that the compiler thinks it has a value complex , we can get Complex as typeof complex :现在编译器认为它有一个值complex ,我们可以得到Complex作为typeof complex

type Complex = typeof complex;
/* type Complex = {
    a: string;
    modifiedField: false;
} */

Yay, it's the same type as ManualComplex .是的,它与ManualComplex类型相同。 😺🌹 Is it worth it? 😺🌹值得吗? No. 😿🥀 Oh well, best I can do.不。😿🥀 哦,我能做的最好。 I'd definitely stick with the non-piped version you started with.我肯定会坚持使用您开始使用的非管道版本。 Good luck!祝你好运!

Playground Link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM