TypeScript和字段初始值设定项

如何以这种方式在TS中创build一个新的类(例如在C#中显示我想要的):

 // ... some code before return new MyClass { Field1 = "ASD", Field2 = "QWE" }; // ... some code after 

解:
经典的JavaScript语法:

 return { Field1: "ASD", Field2: "QWE" }; 

在TypeScript codeplex上有一个问题描述了这一点: 支持对象初始化器 。

如上所述,您可以通过在TypeScript中使用接口而不是类来实现这一点:

 interface Name { first: string; last: string; } class Person { name: Name; age: number; } var bob: Person = { name: { first: "Bob", last: "Smith", }, age: 35, }; 

更新07/12/2016: Typescript 2.1引入了映射types,并提供了Partial<T> ,它允许你这样做….

 class Person { public name: string = "default" public address: string = "default" public age: number = 0; public constructor(init?:Partial<Person>) { Object.assign(this, init); } } let persons = [ new Person(), new Person({}), new Person({name:"John"}), new Person({address:"Earth"}), new Person({age:20, address:"Earth", name:"John"}), ]; 

原始答案:

我的方法是定义一个单独的fieldsvariables,你传递给构造函数。 诀窍是重新定义此初始化程序的所有类字段为可选。 当对象被创build(使用其默认值)时,只需将初始化对象分配给此对象;

 export class Person { public name: string = "default" public address: string = "default" public age: number = 0; public constructor( fields?: { name?: string, address?: string, age?: number }) { if (fields) Object.assign(this, fields); } } 

或手动(比较安全):

 if (fields) { this.name = fields.name || this.name; this.address = fields.address || this.address; this.age = fields.age || this.age; } 

用法:

 let persons = [ new Person(), new Person({name:"Joe"}), new Person({ name:"Joe", address:"planet Earth" }), new Person({ age:5, address:"planet Earth", name:"Joe" }), new Person(new Person({name:"Joe"})) //shallow clone ]; 

和控制台输出:

 Person { name: 'default', address: 'default', age: 0 } Person { name: 'Joe', address: 'default', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 0 } Person { name: 'Joe', address: 'planet Earth', age: 5 } Person { name: 'Joe', address: 'default', age: 0 } 

这给你基本的安全和属性初始化,但它的所有可选的,可以是乱序。 如果你不传递一个字段,你将得到这个类的默认值。

您也可以将它与所需的构造函数参数混合在一起 – 最后粘住fields

大概和C#样式接近,我想( 实际的field-init语法被拒绝了 )。 我更喜欢适当的现场初始化,但看起来不会发生。

为了进行比较,如果使用了强制转换方法,则初始化对象必须包含所有要转换types的字段,并且不会获得由类本身创build的任何类特定函数(或派生类)。

在某些情况下,使用Object.create可能是可以接受的。 如果您需要后向兼容性或想要推出您自己的初始化函数,Mozilla参考包含一个polyfill。

适用于你的例子:

 Object.create(Person.prototype, { 'Field1': { value: 'ASD' }, 'Field2': { value: 'QWE' } }); 

有用的scheme

  • unit testing
  • 内联声明

在我的情况下,我发现这在unit testing有用的原因有两个:

  1. 在testing期望的时候,我经常想要创造一个苗条的对象
  2. unit testing框架(如Jasmine)可能会比较对象的原型( __proto__ )并且不能通过testing。 例如:
 var actual = new MyClass(); actual.field1 = "ASD"; expect({ field1: "ASD" }).toEqual(actual); // fails 

unit testing失败的输出不会产生什么是不匹配的线索。

  1. 在unit testing中,我可以select支持哪些浏览器

最后,在http://typescript.codeplex.com/workitem/334上提出的解决scheme不支持内联json样式的声明。; 例如,以下不能编译:

 var o = { m: MyClass: { Field1:"ASD" } }; 

我更倾向于这样做,使用(可选)自动属性和默认值。 你还没有build议这两个字段是数据结构的一部分,所以这就是我select这种方式的原因。

你可以在类上有属性,然后按照通常的方式分配它们。 显然他们可能或可能不需要,所以这也是别的。 只是这是一个很好的语法糖。

 class MyClass{ constructor(public Field1:string = "", public Field2:string = "") { // other constructor stuff } } var myClass = new MyClass("ASD", "QWE"); alert(myClass.Field1); // voila! statement completion on these properties 

你可以影响你的类types中的匿名对象。 奖励 :在视觉工作室中,您可以通过智能感知获益:)

 var anInstance: AClass = <AClass> { Property1: "Value", Property2: "Value", PropertyBoolean: true, PropertyNumber: 1 }; 

编辑:

警告如果类有方法,你的类的实例将不会得到它们。

这个解决scheme只能用于接口 。 例如,使用此解决scheme将模型存储为普通旧对象。

 interface IClass { Property1: string; Property2: string; PropertyBoolean: boolean; PropertyNumber: number; } var anObject: IClass = <IClass> { Property1: "Value", Property2: "Value", PropertyBoolean: true, PropertyNumber: 1 }; 

我build议一种不需要Typescript 2.1的方法:

 class Person { public name: string; public address?: string; public age: number; public constructor(init:Person) { Object.assign(this, init); } public someFunc() { // todo } } let person = new Person(<Person>{ age:20, name:"John" }); person.someFunc(); 

关键点:

  • Typescript 2.1不需要, Partial<T>不需要
  • 它支持函数(与不支持函数的简单types断言相比)

最简单的方法是使用types转换。

 return <MyClass>{ Field1: "ASD", Field2: "QWE" }; 

下面是一个解决scheme,它将Object.assign一个较短的应用程序结合到更加紧密地模拟原始C#模式。

但首先回顾一下迄今为止提供的技术,其中包括:

  1. 复制接受对象的构造函数并将其应用于Object.assign
  2. 复制构造函数中的一个巧妙的Partial<T>技巧
  3. 对POJO使用“投射”
  4. 利用Object.create而不是Object.assign

当然,每个人都有自己的优点/缺点。 修改一个目标类来创build一个拷贝构造函数可能并不总是一个选项。 而“铸造”失去了与目标types相关的任何function。 Object.create似乎不太吸引人,因为它需要一个相当详细的属性描述符映射。

最短,通用答案

所以,这里有另外一个比较简单的方法,维护types定义和相关的函数原型,并且更精确地模拟预期的C#模式:

 const john = Object.assign( new Person(), { name: "John", age: 29, address: "Earth" }); 

而已。 除了C#模式之外,唯一的附加是Object.assign以及2个括号和一个逗号。 查看下面的工作示例,以确认它维护types的函数原型。 不需要构造函数,也不需要聪明的技巧。

工作示例

这个例子展示了如何使用C#字段初始值设定项的近似值初始化一个对象:

 class Person { name: string = ''; address: string = ''; age: number = 0; aboutMe() { return `Hi, I'm ${this.name}, aged ${this.age} and from ${this.address}`; } } // typescript field initializer (maintains "type" definition) const john = Object.assign( new Person(), { name: "John", age: 29, address: "Earth" }); // initialized object maintains aboutMe() function prototype console.log( john.aboutMe() );