typescript 是否可以省略任何已指定子字符串的属性?

k2fxgqgv  于 2023-02-13  发布在  TypeScript
关注(0)|答案(1)|浏览(91)

我想创建一个接口,它接受任何泛型对象。在create方法中,它应该接受与泛型类型匹配的对象,但应忽略任何以“id”作为子字符串的属性。因此,问题是不同的接口可能具有不同形状的属性“id”,如:id、_id、__id、实体id等。因此,我希望具有如下内容:

interface IBaseRepository<T extends object> {
  create(input: OmitAnyPropertyWithSpecifiedSubstring<T, 'id'>): Promise<T>;
}

是否有可能实施?如果有,如何实施?

a6b3iqyw

a6b3iqyw1#

您可以将键重新Map与as一起使用,将不需要的键转换为never,这样做的效果是将属性从Map类型中排除。模式template literal type${string}${S}${string}将匹配S作为子字符串出现的任何字符串(只要S是某个特定的字符串类型),所以你可以使用Exclude<T, U>实用程序类型来抑制任何可分配给该类型的键:

type OmitAnyPropertyWithSpecifiedSubstring<T extends object, S extends string> =
    { [K in keyof T as Exclude<K, `${string}${S}${string}`>]: T[K] }

让我们来测试一下:

interface Foo {
    prop: number,
    entityid: string,
    id: number,
    _id: string,
    idiosyncraticPropertyName: boolean,
    appearsInTheMiddle: number,
    hardToAvoid: string
    otherProp: string
}

type OFoo = OmitAnyPropertyWithSpecifiedSubstring<Foo, "id">;
/* type OFoo = {
    prop: number;
    otherProp: string;
} */

注意只有propotherProp保留了下来,因为所有其他属性都包含子字符串"id"。显然,您不喜欢像"hardToAvoid""inTheMiddle"这样的东西被抑制,因为您并不"打算"让它们包含"id"。♂我不确定您要如何处理这个问题,🤷‍但我要说的是,这绝对超出了问题的范围,然后继续。️ I'm not sure how you want to handle that, but I'm going to say that it's firmly outside the scope of the question as asked and move on.
让我们确保它在您的示例中有效:

interface IBaseRepository<T extends object> {
    create(input: OmitAnyPropertyWithSpecifiedSubstring<T, 'id'>): Promise<T>;
}

declare const x: IBaseRepository<Foo>;
x.create({ prop: 1, otherProp: "abc", entityid: "oops" });
// error! --------------------------> ~~~~~~~~~~~~~~~~
// Object literal may only specify known properties, and 'entityid' 
// does not exist in type 'OmitAnyPropertyWithSpecifiedSubstring<Foo, "id">'

看起来不错。编译器抱怨entityid,因为按照需要对对象文本进行了过多的属性检查。
但请注意,多余的属性通常不是无效的,因此如果使用中间变量,您可能不会收到警告...这是TypeScript的预期行为,您可以在Typescript: prevent assignment of object with more properties than is specified in target interface中了解更多信息
Playground代码链接

相关问题