如果我想在Javascript中以编程方式将一个属性分配给一个对象,我会这样做:

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

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

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

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


当前回答

当对象具有特定类型时,此解决方案非常有用。比如从其他源获取对象时。

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

其他回答

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

如果你必须先创建一个空对象,那么从这两个解决方案中选择一个。记住,每次你使用as,你就失去了安全。

安全解决方案

对象类型在getObject中是安全的,这意味着对象。A的类型为字符串| undefined

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

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

短期解决方案

对象类型在getObject中是不安全的,这意味着对象。即使在赋值之前,A的类型也是string。

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

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

若要保留先前的类型,请将对象临时转换为any

  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;

我倾向于把任何放在另一边,即var foo:IFoo = <任何>{};所以这样的东西仍然是类型安全的:

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 = {};

在网上试试

这是Object的一个特殊版本。赋值,它会在每次属性更改时自动调整变量类型。不需要额外的变量、类型断言、显式类型或对象副本:

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断言函数。与Object.assign不同,assign的返回类型是void。

当一个Map可以接受固定类型的任意值时,使用ES6 Map,否则使用可选属性

我想这就是我的指导方针。ES6映射可以在typescript中完成,如在typescript中提到的:ES6映射

可选属性的主要用例是函数的“选项”参数:使用命名参数JavaScript(基于typescript)在这种情况下,我们确实提前知道允许的属性的确切列表,所以最明智的事情是只定义一个显式接口,只是使任何可选的可选与?正如在https://stackoverflow.com/a/18444150/895245上提到的,尽可能多地进行类型检查:

const assert = require('assert')

interface myfuncOpts {
  myInt: number,
  myString?: string,
}

function myfunc({
  myInt,
  myString,
}: myfuncOpts) {
  return `${myInt} ${myString}`
}

const opts: myfuncOpts = { myInt: 1 }
if (process.argv.length > 2) {
  opts.myString = 'abc'
}

assert.strictEqual(
  myfunc(opts),
  '1 abc'
)

然后,当它是真正任意的(无限多个可能的键)并且具有固定类型时,我将使用Map,例如:

const assert = require('assert')
const integerNames = new Map<number, string>([[1, 'one']])
integerNames.set(2, 'two')
assert.strictEqual(integerNames.get(1), 'one')
assert.strictEqual(integerNames.get(2), 'two')

测试:

  "dependencies": {
    "@types/node": "^16.11.13",
    "typescript": "^4.5.4"
  }