首页 > 解决方案 > 转换接口,使得一个类型的出现被另一个类型的打字稿替换

问题描述

我有一个函数可以遍历给定的泛型对象(T 类型),然后将某种类型的某些道具转换为不同的类型。我需要这个函数来更新输入类型 (T) 以反映函数所做的输入变化。

我正在尝试完成以下任务:给定Interface1

interface Interface1 {
  prop1: Type1
  prop2: Type2
  prop3: {
    subprop1: Type1
    subprop2: Type2
  }
}

然后我需要一个类型表达式Transform<T>,这样Transform<Interface1>会导致:

interface Interface2 {
  prop1: NewType
  prop2: Type2
  prop3: {
    subprop1: NewType
    subprop2: Type2
  }
}

标签: typescript

解决方案


This can be done - but at what cost?

It might be worth trying to see if there's a way you can do what you want to do in a simpler manner. There's not a lot of context so I can't advise on how you would approach this - I usually try and stay away from stuff like this for maintainability reasons.

This will do what you're asking - where T is the object type to transform, TTarget is Type1 and TResult is NewType

First - the simpler form which lazily evaluates. This means you get the following output when hovering over the result type:

enter image description here

type Transform1<T extends object, TTarget, TReplacement> = {
    [key in keyof T]:
        T[key] extends object
            ? Transform1<T[key], TTarget, TReplacement>
            : T[key] extends TTarget ? TReplacement : T[key];
} 

This version will eagerly evaluate the nested object - giving you more 'correct' looking type hints

enter image description here

type Transform2<T extends object, TTarget, TReplacement> = {
    [key in keyof T]:
        T[key] extends object
            ? {
                [innerKey in keyof Transform2<T[key], TTarget, TReplacement>]:
                    Transform2<T[key], TTarget, TReplacement>[innerKey]
              }
            : T[key] extends TTarget ? TReplacement : T[key];
} 

How this works is that we first map over T using a mapped type -

{ [key in keyof T]: T[key] }

Next we need to check if T[key] is a nested object, and if so - we need to recurse the type. We do this by asking if T[key] extends object. If it does, Transform<T[key], TTarget, TResult> again.

Then we can check if T[key] is assignable to the TTarget we are trying to replace with T[key] extends TTarget. If it does, return TReplacement, otherwise leave it alone by returning T[key].

The eagerly evaluated version just uses a little trick where we map over the result type again.

We are doing the same {[key in T]: T[key]} - except we replace T with the thing we want to evaluate - which is Transform<T[key], TTarget, TResult>. We also need another variable for key, because we're now mapping over the inner type, so I used innerKey here.

This gives us

{ [innerKey in keyof Transform2<T[key], TTarget, TReplacement>]: Transform2<T[key], TTarget, TReplacement>[innerKey] }

Playground link: https://www.typescriptlang.org/play?#code/C4TwDgpgBAKuEEYoF4oGdgCcCWA7A5gNwBQoks8ATClAEYD29ANhAIa4lnQByEA7nHKpcAVwC2tCJhLE8wKQDNWAY2gBJXPMxLVSAN7EoUMJnpgEALgqQEh46bCUrgiJTsmzAZisGjRtCK0HubO8LZ+6IHBTtaudgC+xImymooq6qna6dS+9maWULwC8O4OMS5uRsHeULn+UQ4FRS529UFloZCVUInJAPR9sAAW2GhQfNhMTFBMrABekyBQEABurEwirPJQwEPQuBAYEAAmUPS0AFYQysCk8LCY7GgK9JhiCAA8MMsAHvK4xzG5yuNwANLAYKxMPgIMBwTAAEoQMCzVRiCCaAB8NDqUAA2gBrCBLPBQIkgegKWAAXQsrQisEJxOpv3+gLOl2utwZPKgAH4Hk8Xm9PjAmSBqfDIdDYfCkSj0uisfTeVBnOKWRA-hj2TBpTDgPzYPLURAlYb1eTqSR4lBiMQBsNRuNJtM2DDMEwlqt1pttrt9od5KdgVyoABaKD4bArPD4KAUkRQMSvaAAcmUr0wXLTM0YBLjO3uI00aDu5Bgj1wz1eYkoX1ZOqBnLBEKhBrlyNN5uxqFx4qgpPJlJpdNVYqtjYBzZB3NVfgFuPnfjxeAOmAA0sTB7gycSR5WhbX6xPmVL27LjV3FRjgJjaSrlwzD9XhXWvhrzzK4VeFWjb5iq64OuW4So+qqJE+aqMpOWpsmMeoXoaAqIte-6aNBp4Sjadr2o6AAS9ArFIZzEZgUBIgETDAEgwD0OgEDQFwUAlrczGUSI1FIKgL41iKHwaFoOiIPCYTgs08CYjIBFESRsnkRx1HUHRDFMcWcjltAinANQPFVnx76CWkuiiTY4n8C4UlAA


推荐阅读