TypeScript 中的通用对象类型

82
在 TypeScript 中,是否有一种方法可以将一个变量赋值为泛型对象类型?这里所说的“泛型对象类型”是什么意思?
let myVariable: GenericObject = 1 // Should throw an error
                              = 'abc' // Should throw an error
                              = {} // OK
                              = {name: 'qwerty'} //OK

即,它应仅允许将JavaScript对象分配给变量,而不允许分配其他类型的数据(数字、字符串、布尔值)。

5个回答

111

当然可以:

type GenericObject = { [key: string]: any };

let myVariable1: GenericObject = 1; // Type 'number' is not assignable to type '{ [key: string]: any; }'
let myVariable2: GenericObject = 'abc'; // Type 'string' is not assignable to type '{ [key: string]: any; }'
let myVariable3: GenericObject = {} // OK
let myVariable4: GenericObject = {name: 'qwerty'} //OK

(在playground中查看代码)


在TS 4.1.3上,当我尝试将其传递给new Proxy()时,会出现错误TS2345:类型为“GenericObject”的参数无法分配给类型为“object”的参数。有什么想法吗? - Romasato
1
@Romasato,当我在TS Playground上尝试(使用TS 4.1.2)时,它运行良好,您能否在那里重现它? - Nitzan Tomer
1
GenericObject 在这里与 Record<string, any> 相同。如果您没有使用任何内容,也可以使用 Record<string, unknown>。 - user2157850

82

Typescript 2.1+还有一个实用类型叫做Record<K, T>,您可以使用它来替代创建自己的定义

const myObj: Record<string, any>;

当可以给key赋予一个有意义的名称时,我喜欢使用最佳答案中描述的样式,但如果这并不是很明显或必要的话,Record是一个很好的选择。


4
从TypeScript 3.0+开始,这是类型安全的答案:
type GenericObject = Record<string, unknown>;

由于您将获得类型保护,因此在使用对象的属性之前,您需要进行类型检查:

const obj: GenericObject = {
  someFn: () => 'string return';
}

if (typeof obj.someFn === 'function') {
  obj.someFn();
}

TypeScript当然不会抱怨any,但从技术上讲,它并不是“通用对象类型”。更多关于anyunknown之间差异的信息,请参考以下链接:请注意,保留了HTML标签。

0

有点离题,因为我在其他地方没有找到类似的答案,在这里引用@JaredMcAteer的回答,使用record帮助我混合枚举+对象。

enum FOO_ENUM {
  BAR = 'BAZ';
}

type FOO_OBJECT_TYPE = { ... };

const BIZ_OBJECT: Record<FOO_ENUM, FOO_OBJECT_TYPE> = {
  [FOO_ENUM.BAR]: { ... }
}

之前我会输入 BIZ_OBJECT,如下所示:
BIZ_OBJECT: {[type: string]: FOO_OBJECT}
这允许像 BIZ_OBJECT.asd 这样的用法。现在只能使用来自FOO_ENUM的键名,例如:

  • BIZ_OBJECT.BAZ // { ... }
  • BIZ_OBJECT.asd // 属性 'asd' 在类型上不存在...
  • BIZ_OBJECT[FOO_ENUM.BAR] // { ... }
  • BIZ_OBJECT[FOO_ENUM.asd] // 属性 'asd' 在类型上不存在...
  • BIZ_OBJECT[FOO_ENUM['asd']] // ! not caught !

1
抛开主题,但我建议像 FooObjectType 这样为你的类型命名,枚举则像 FooEnum。个人认为带有 ALL_CAPS 的类型名称太嘈杂了。 - Franklin Yu

0
从TypeScript 2.2开始,您可以使用


let myVariable: object;

编辑:这里有一个例子:

let myVariable: object = { fun: 1 };

7
这段代码无法运行,例如:const me: object = {}; me.fun = 1会抛出一个错误,提示“在类型为 'object' 的变量上不存在属性 'fun'”。 - Samuel Thompson
2
但是你可以这样做:let myVariable: object = { fun: 1 };,这就是 OP 所问的。 - Bill Barnes
内置的 object 类型也没有定义索引签名,所以它不是一个非常好的通用对象占位符,很遗憾。 - brainbag
1
@BillBarnes 没有人会创建一个空对象,OP在第3行展示了一个空对象,因此这不是OP所要求的。 - Ivan Castellanos
不要使用object来做这个。它存在的原因是为了消除原始类型:https://www.youtube.com/watch?v=0y5hhzuQjCA - basarat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接