打字稿:通用于特定类型

时间:2020-08-31 21:24:50

标签: typescript

我有一个通用类型

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  },
};

我使用通用类型来帮助构建/类型检查我创建的新对象。

const Obj1: GenericType = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

有效。但是,当我使用新对象时,vscode / typescript在不删除Obj1的情况下不会显示GenericType的键或道具。我也可以“扩展”类型,但这就是代码重复。

Obj1.???

是否可以在通过我从它创建的新对象访问更特定的道具的同时保留GenericType?


更新1

我希望vscode /打字稿能够显示/验证

Obj1.key1.prop1
Obj1.key2.prop1

,如果出现错误,则

Obj1.key1.prop2
Obj1.key2.prop3
Obj1.key2.prop321

3 个答案:

答案 0 :(得分:1)

我看到这种情况的用例是,您希望GenericType拥有任何类型的字符串作为键,但仍想确定在声明它们时这些键的值可以是什么。

在这种情况下,您可以利用Record类型将Obj1的允许键限制为仅您指定的键。

type GenericType<K extends string> = Record<K, {
  prop1: string,
  prop2?: string,
  prop3?: number,
}>

然后,在定义Obj1时,可以通过将允许键的并集设置为第一类型参数来指定允许键的类型。

const Obj1: GenericType<"key1" | "key2"> = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

TypeScript现在可让您以完全类型安全的方式访问key1key2

Obj1.key1
// (property) key1: {
//     prop1: string;
//     prop2?: string | undefined;
//     prop3?: number | undefined;
// }

编辑

根据OP的评论,听起来他宁愿不指定所有键名,也不必手动检查可选字段的存在。

我想做到这一点,同时又确保所声明的对象与GenericType接口的约束匹配的最好方法是执行以下操作。

首先,您需要以下实用程序类型:

type Constraint<T> = T extends Record<string, {
  prop1: string,
  prop2?: string,
  prop3?: number,
}> ? T : never

如果never与约束不匹配,则返回T,否则返回T

现在,您声明实际需要的普通对象。没有类型注释。

const CorrectObj = {
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
};

然后,您将此对象常量分配给另一个变量,但声明新变量的类型必须为Constraint<typeof CorrectObj>

const CheckedObj: Constraint<typeof CorrectObj> = CorrectObj

如果CorrectObj符合约束条件,则CheckedObj将是CorrectObj的简单副本,其中所有字段都可用。

如果对象文字不符合约束条件,但是在尝试将CheckedBadObj分配给文字时会出现类型错误:

const BadObj = {
  key1: {
    progfdgp1: "hi",
  },
  key2: {
    prop1: "bye",
    prdfgop2: "sup",
  },
};

const CheckedBadObj: Constraint<typeof BadObj> = BadObj
//    ^^^^^^^^^^^^^
// Type '{ key1: { progfdgp1: string; }; key2: { prop1: string; prdfgop2: string; }; }' is not assignable to type 'never'. (2322)

解释是Constraint<T>不匹配时Tnever,但是您仍在尝试为{{1}分配非never值},导致类型冲突!

这在声明每个对象文字的两个实例时涉及一些重复,但这是唯一的方法

  1. 让TypeScript准确地 对象上存在哪些字段,包括所有子对象,而
  2. 检查“通用”类型的值 符合您的限制

您可以在playground中使用这种方法。

答案 1 :(得分:1)

假设您的目标是在key1的自动完成菜单中显示key2Obj1,但是稍后仍然可以设置其他键,这是一个可能的解决方案:

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  };
};

const generify = <T extends GenericType>(obj: T): T & GenericType => obj;

const Obj1 = generify({
  key1: {
    prop1: "hi",
  },
  key2: {
    prop1: "bye",
    prop2: "sup",
  },
});

我现在无法想到一个更简单的解决方案,该解决方案将使Obj1与仅包含GenericTypekey1属性的特定类型之间具有相同的交集类型。

答案 2 :(得分:0)

使用通用实用程序功能( * @ORM\Entity(repositoryClass=UserRepository::class) * @ORM\Table(name="difc_user") * @AttributeOverrides({ * @AttributeOverride(name="password", * column=@ORM\Column( * nullable=true * ) * ), * } * ) * * @UniqueEntity("email")

  • 使用通用约束(enforce)确保对象与GenericType匹配
  • 并返回传入的对象的类型以进行类型推断。

代码:

extends

工作解决方案:

type GenericType = {
  [key: string]: {
    prop1: string,
    prop2?: string,
    prop3?: number,
  };
};

const enforce = <T extends GenericType>(obj: T): T => obj;