如何在TypeScript中为对象动态分配属性?


359

如果我想以编程方式将属性分配给Javascript中的对象,则可以这样做:

var obj = {};
obj.prop = "value";

但是在TypeScript中,这会产生一个错误:

类型“ {}”的值不存在属性“ prop”

我应该如何在TypeScript中为对象分配任何新属性?


1
仅供参考。如果您想关注它,我已经打开了一个与此相关的讨论
joshuapoehls 2012年

Answers:


454

可以将表示objany,但这违背了使用打字稿的全部目的。obj = {}暗示obj是一个Object。将其标记为any没有任何意义。为了实现期望的一致性,可以如下定义接口。

interface LooseObject {
    [key: string]: any
}

var obj: LooseObject = {};

或使其紧凑:

var obj: {[k: string]: any} = {};

LooseObject可以接受带有任何字符串的字段作为键并any输入类型作为值。

obj.prop = "value";
obj.prop2 = 88;

该解决方案的真正优雅之处在于,您可以在界面中包含类型安全字段。

interface MyType {
    typesafeProp1?: number,
    requiredProp1: string,
    [key: string]: any
}

var obj: MyType ;
obj = { requiredProp1: "foo"}; // valid
obj = {} // error. 'requiredProp1' is missing
obj.typesafeProp1 = "bar" // error. typesafeProp1 should be a number

obj.prop = "value";
obj.prop2 = 88;

虽然这回答了原始问题,但@GreeneCreations 在这里给出的答案可能会为如何解决该问题提供另一种观点。


13
我认为这是最好的解决方案。我认为当时有人问这个问题,尚未在TypeScript中实现这样的索引属性。
彼得·奥尔森

像错误这样的本机对象怎么样
Wayou

@Wayou本机对象(例如Error和Array)从Object继承。因此LooseObject也可以采取一个错误。
Akash Kurian Jose

您可以检查对象是否具有某些(动态)属性吗?
phhbr '18年

1
对于这些松散的对象,您如何用getter-setter形式编写它们?
JokingBatman

81

或一劳永逸:

  var obj:any = {}
  obj.prop = 5;

41
如果any要使用很多类型的东西,TypeScript有什么意义?:/
AjaxLeung

16
@AjaxLeung如果您需要这样做,那么您使用的是错误的。
亚历克斯

2
请参见上面的其他保留对象先前类型的编辑。
Andre Vianna

6
@AjaxLeung您应该很少投给any。TypeScript用于在编译时捕获(潜在)错误。如果您强制转换any为静音错误,那么您将失去打字的能力,并可能会回到纯JS。any仅在导入无法编写TS定义的代码或在将代码从JS迁移到TS的同时才应理想地使用
Precastic

63

我倾向于放在any另一侧,也就是说,var foo:IFoo = <any>{};这样的东西仍然是类型安全的:

interface IFoo{
    bar:string;
    baz:string;
    boo:string;     
}

// How I tend to intialize 
var foo:IFoo = <any>{};

foo.bar = "asdf";
foo.baz = "boo";
foo.boo = "boo";

// the following is an error, 
// so you haven't lost type safety
foo.bar = 123; 

另外,您可以将这些属性标记为可选:

interface IFoo{
    bar?:string;
    baz?:string;
    boo?:string;    
}

// Now your simple initialization works
var foo:IFoo = {};

在线尝试


5
+1是唯一保持类型安全的解决方案。只需确保在它之后立即实例化所有非可选属性,以免以后被错误咬住。
Aidiakapi 2014年

这真的有效吗?编译后,我的JavaScript中仍然有<any> {}。
2015年

4
I still have <any>{那么您还没有编译。TypeScript会删除其发出的内容
basarat 2015年

4
这些天,var foo: IFoo = {} as any是首选。类型转换的旧语法与TSX(Typescript定义的JSX)相冲突。

51

当您的对象具有特定类型时,此解决方案很有用。就像在获取对象到其他来源时一样。

let user: User = new User();
(user as any).otherProperty = 'hello';
//user did not lose its type here.

7
这是一次性分配的正确解决方案。
soundly_typed

这个答案对我有用,因为对于Facebook登录,我必须将属性添加到window对象。我在TypeScript中第一次使用as关键字。
AlanObject '19

33

尽管编译器抱怨它仍应按您的要求输出。但是,这将起作用。

var s = {};
s['prop'] = true;

2
是的,这不是真正的键入脚本方式,您会失去智能。
pregmatch

25

另一个选择是将属性作为集合进行访问:

var obj = {};
obj['prop'] = "value";


2
这是最简洁的方法。Object.assign(obj, {prop: "value"})从ES6 / ES2015开始也可以。
查尔斯

8

我很惊讶没有答案引用Object.assign,因为当我想到JavaScript中的“组合”时,这就是我使用的技术。

它可以在TypeScript中按预期工作:

interface IExisting {
    userName: string
}

interface INewStuff {
    email: string
}

const existingObject: IExisting = {
    userName: "jsmith"
}

const objectWithAllProps: IExisting & INewStuff = Object.assign({}, existingObject, {
    email: "jsmith@someplace.com"
})

console.log(objectWithAllProps.email); // jsmith@someplace.com

优点

  • 始终确保输入的安全性,因为您根本不需要使用该any类型
  • 使用TypeScript的聚合类型(&在声明的类型时用表示objectWithAllProps),这清楚地表明我们正在动态(即动态)编写新类型

要注意的事情

  1. Object.assign具有自己的独特之处(大多数有经验的JS开发人员众所周知),在编写TypeScript时应予以考虑。
    • 它可以以可变方式使用,也可以以不可变的方式使用(我在上面演示了不可变的方式,这意味着existingObject保持不变,因此不具有email属性。对于大多数函数式程序员而言,这是一件好事,因为结果是唯一的新变化)。
    • 当您使用扁平对象时,Object.assign效果最佳。如果要合并两个包含可为空的属性的嵌套对象,最终可能会用undefined覆盖真实值。如果您注意Object.assign参数的顺序,那应该没问题。

对我而言,在需要使用它来显示数据的情况下,它似乎工作正常,但是当您需要将修改后的类型的条目添加到数组中时,这似乎不太好用。我诉诸于动态地组成对象并在一行中分配它,然后在连续的行中分配动态属性。这使我到了大部分时间,所以谢谢。
Shafiq Jetha

8

您可以使用传播运算符基于旧对象创建新对象

interface MyObject {
    prop1: string;
}

const myObj: MyObject = {
    prop1: 'foo',
}

const newObj = {
    ...myObj,
    prop2: 'bar',
}

console.log(newObj.prop2); // 'bar'

TypeScript将推断原始对象的所有字段,而VSCode将执行自动补全,等等。


很好,但是如果您需要在prop3中使用prop2,将很难实现
ya_dimon


5

可以通过以下方式将成员添加到现有对象

  1. 扩展类型(阅读:扩展/专业化接口)
  2. 将原始对象转换为扩展类型
  3. 将成员添加到对象
interface IEnhancedPromise<T> extends Promise<T> {
    sayHello(): void;
}

const p = Promise.resolve("Peter");

const enhancedPromise = p as IEnhancedPromise<string>;

enhancedPromise.sayHello = () => enhancedPromise.then(value => console.info("Hello " + value));

// eventually prints "Hello Peter"
enhancedPromise.sayHello();

1
对我来说最好的解决方案 我今天学到了一些新东西,谢谢
莫里斯(Maurice)'18

我收到此代码的TS2352错误
ya_dimon

@ya_dimon尝试它的打字稿游乐场:typescriptlang.org/play它工作正常!
Daniel Dietrich

@DanielDietrich现在可以工作.. thx,不知道发生了什么。
ya_dimon

别客气!发生转变;)
丹尼尔·迪特里希

5

由于您无法执行此操作:

obj.prop = 'value';

如果您的TS编译器和linter不严格,则可以这样编写:

obj['prop'] = 'value';

如果您的TS编译器或linter严格,则另一个答案是键入:

var obj = {};
obj = obj as unknown as { prop: string };
obj.prop = "value";

3
这是,如果“noImplicitAny:假”的tsconfig.json
雷米尔ADANE

否则,您可以回答GreeneCreations。
LEMUEL ADANE

4

将任何新属性存储在任何类型的对象上,方法是将其类型转换为“ any”:

var extend = <any>myObject;
extend.NewProperty = anotherObject;

稍后,您可以通过将扩展对象转换回'any'来检索它:

var extendedObject = <any>myObject;
var anotherObject = <AnotherObjectType>extendedObject.NewProperty;

这完全是正确的解决方案。假设您有一个对象let o:ObjectType; ....之后,您可以将o强制转换为任何(<any> o).newProperty ='foo'; 可以像(<any> o).newProperty一样进行检索。没有编译器错误,并且像魅力一样起作用。
马特·阿什利

这会制动智能感知...除了保持智能感知之外,还有什么办法吗?
pregmatch

4

为了确保类型是Object(即键值对),请使用:

const obj: {[x: string]: any} = {}
obj.prop = 'cool beans'

4
  • 情况1:

    var car = {type: "BMW", model: "i8", color: "white"}; car['owner'] = "ibrahim"; // You can add a property:

  • 情况2:

    var car:any = {type: "BMW", model: "i8", color: "white"}; car.owner = "ibrahim"; // You can set a property: use any type



3

您可以添加此声明以使警告静音。

declare var obj: any;



3

为了保留您以前的类型,请将您的对象临时转换为任何类型

  var obj = {}
  (<any>obj).prop = 5;

只有在使用强制转换时,新的动态属性才可用:

  var a = obj.prop; ==> Will generate a compiler error
  var b = (<any>obj).prop; ==> Will assign 5 to b with no error;

3

这是的特殊版本Object.assign,可在每次属性更改时自动调整变量类型。不需要其他变量,类型断言,显式类型或对象副本:

function assign<T, U>(target: T, source: U): asserts target is T & U {
    Object.assign(target, source)
}

const obj = {};
assign(obj, { prop1: "foo" })
//  const obj now has type { prop1: string; }
obj.prop1 // string
assign(obj, { prop2: 42 })
//  const obj now has type { prop1: string; prop2: number; }
obj.prop2 // number

//  const obj: { prop1: "foo", prop2: 42 }

注意:该示例使用TS 3.7 断言函数。的返回类型assignvoid,不像Object.assign


1

如果您使用的是Typescript,则大概是要使用类型安全性。在这种情况下,会标明裸对象和“ any”。

最好不要使用Object或{},而应使用某些命名类型。或者您可能正在使用具有特定类型的API,需要使用自己的字段进行扩展。我发现这有效:

class Given { ... }  // API specified fields; or maybe it's just Object {}

interface PropAble extends Given {
    props?: string;  // you can cast any Given to this and set .props
    // '?' indicates that the field is optional
}
let g:Given = getTheGivenObject();
(g as PropAble).props = "value for my new field";

// to avoid constantly casting: 
let k:PropAble = getTheGivenObject();
k.props = "value for props";

1

动态地为TypeScript中的对象分配属性。

为此,您只需要使用如下的打字稿接口:

interface IValue {
    prop1: string;
    prop2: string;
}

interface IType {
    [code: string]: IValue;
}

你可以这样使用

var obj: IType = {};
obj['code1'] = { 
    prop1: 'prop 1 value', 
    prop2: 'prop 2 value' 
};

1
我尝试使用您的代码,但没有收到任何错误:pastebin.com/NBvJifzN
pablorsk

1
尝试初始化attributes里面的字段SomeClass ,应该修复它public attributes: IType = {}; pastebin.com/3xnu0TnN
Nerdroid

1

尝试这个:

export interface QueryParams {
    page?: number,
    limit?: number,
    name?: string,
    sort?: string,
    direction?: string
}

然后用

const query = {
    name: 'abc'
}
query.page = 1

0

唯一的完全类型安全的解决方案是this,但是有点罗word,并迫使您创建多个对象。

如果必须先创建一个空对象,则选择这两个解决方案之一。请记住,每次使用as都会失去安全性。

更安全的解决方案

内部的类型object安全的getObject,这意味着object.a将是类型string | undefined

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object: Partial<Example> = {};
  object.a = 'one';
  object.b = 1;
  return object as Example;
}

短期解决方案

该类型object不是安全的内部getObject装置,该装置object.a将型的string,甚至它的任务之前。

interface Example {
  a: string;
  b: number;
}

function getObject() {
  const object = {} as Example;
  object.a = 'one';
  object.b = 1;
  return object;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.