Typescript类型在保留对象结构的同时使用平面Map *(删除一个级别但保留其子级别)

yptwkmov  于 2023-01-10  发布在  TypeScript
关注(0)|答案(2)|浏览(131)

编写一个泛型类型Remove〈T,key〉,它(a)删除所有出现的“key:图元类型”例如“键:编号;”在T中,以及(b)如果T具有“键:“U”,其中U不是基元类型,则将其转换为“U”,并移除“key”。
例如,如果我有以下类型:

type Before = {
  A: string
  B: {
    C: string
    D: {
      E: number
    }
  }
}

我想把它改成这样,比如Remove<Before, "D">

type After = {
  A: string
  B: {
    C: string
    E: number
  }
}

请注意,D已删除,但E仍保留
其他值得一提的案例多亏so_close
病例#1 Remove<T,”data”>

type T = {
 data: {
   data: string;
 }
};

// would be
type T = { };

病例2 Remove<T,”b”>

type T2 = {
  a: {
    b: string;
  };
  b: number;
}

// would be
type T2 = {
  a: { };
}
w9apscun

w9apscun1#

为了清楚起见,我将添加另一个答案;
我们要解决的问题是:* 创建一个类型Remove<T, TKey>
1.
删除T中TKey: PrimitiveType类型的所有条目 *
1.* 递归地展平T中TKey: ComplexType类型的所有条目 *
以下方法应该有效:

type Primitive = number | string | boolean;

// to satisfy Remove<{a : string;}, "a"> === {}
type ConvertPrimitiveToEmptyType<T> = T extends Primitive ? {} : T;

// if T contains a key with name <KeyName> at level 1, proceed recursively
type Unwrap<T, KeyName extends string> = KeyName extends keyof T
  ? Remove<ConvertPrimitiveToEmptyType<T[KeyName]>, KeyName>
  : {};

// separately process two parts of T:
// * part of T with all the keys except <KeyName>
// * part T[KeyName] if it exists 
type Remove<T, KeyName extends string> = {
    [key in keyof Omit<T, KeyName>]:
     T[key] extends Primitive 
      ? T[key]
      : Remove<T[key], KeyName> // part of T without KeyName
    } & Unwrap<T, KeyName>;

让我们用您的一个例子来测试一下!

type Before1 = {
  A: string
  B: {
    C: string
    D: {
      E: number
    }
  }
}

type ExpectedAfter1 = {
  A: string
  B: {
    C: string
    E: number
  }
}

type After1 = Remove<Before1, "D">;

我们可以使用conditional types测试类型是否相等

// if A extends B, and B extends A, then B is equal to A
type CheckForEquality<A,B> = A extends B? B extends A ? true : false : false;

// if this type is "true" it means After1 is equal to ExpectedAfter1
type IsAfter1Good = CheckForEquality<After1, ExpectedAfter1>;

您可以在此TSPlayground找到更多测试和实时代码

juzqafwq

juzqafwq2#

我猜你最初的问题是:

是否可以编写泛型类型Remove<T, U>,其行为与示例中的行为相同

然而,一个例子并不足以回答这个问题,请考虑下面的例子:

type T = {
 data: {
   data: string;
 }
};

定义了T之后,您所期望的Remove<T, "data">应该如何表现呢?它应该删除“data”字段中最深处的值,从而得到{ data: {}; }吗?还是删除最上面的值,从而得到{}
这是这样的:如果我们有下面的类型呢?

type T2 = {
  a: {
    b: string;
  };
  b: number;
}

Remove<T2, "b">应该如何表现?它应该导致{a: {}; b: number}还是{a: { b: string; }; }
我不能发表评论(我的声誉不高),但请解决你的问题中提到的含糊之处。没有这些,恐怕没有足够的数据来回答。
尝试一下:添加更多的例子,或者口头指定你想要的类型应该如何表现。也许,如果你口头指定的话,你已经有了用文字写下来的实现!
P.S.如果您真正想要的只是使用Typescript内置实用程序类型将Before转换为After,您可以这样做:

type After = Omit<Before, "B"> & {
  B: Omit<Before["B"], "D"> & {
    E: Before["B"]["D"]["E"];
  };
};

这个构造使用Omit来“忘记”特定字段中定义的内容,但是在使用Omit之后,我们立即指定所需的字段,并一直这样做,直到达到所需的级别。
不幸的是,这不是很优雅,但事实是:这就是Typescript所能提供的

相关问题