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"}), ];
原始答案:
我的方法是定义一个单独的fields
variables,你传递给构造函数。 诀窍是重新定义此初始化程序的所有类字段为可选。 当对象被创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有用的原因有两个:
- 在testing期望的时候,我经常想要创造一个苗条的对象
- unit testing框架(如Jasmine)可能会比较对象的原型(
__proto__
)并且不能通过testing。 例如:
var actual = new MyClass(); actual.field1 = "ASD"; expect({ field1: "ASD" }).toEqual(actual); // fails
unit testing失败的输出不会产生什么是不匹配的线索。
- 在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#
模式。
但首先回顾一下迄今为止提供的技术,其中包括:
- 复制接受对象的构造函数并将其应用于
Object.assign
- 复制构造函数中的一个巧妙的
Partial<T>
技巧 - 对POJO使用“投射”
- 利用
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() );