如何以这种方式在TS
中初始化新课程(例如在C#
中显示我想要的内容):
// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ... some code after
SOLUTION:
经典JavaScript
语法:
return { Field1: "ASD", Field2: "QWE" };
答案 0 :(得分:313)
2016年7月12日更新:
Typescript 2.1 introduces Mapped 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
变量,并将其传递给构造函数。诀窍是将此初始化程序的所有类字段重新定义为可选。创建对象(使用其默认值)时,只需将初始化对象分配到this
;
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#风格,我认为(actual field-init syntax was rejected)。我更喜欢适当的字段初始化,但看起来还不会发生。
为了进行比较,如果使用强制转换方法,则初始化对象必须包含要转换为的类型的所有字段,并且不要获取由类本身创建的任何特定于类的函数(或派生)。
答案 1 :(得分:64)
TypeScript codeplex上存在一个问题:Support for object initializers。
如上所述,您可以通过在TypeScript中使用接口而不是类来执行此操作:
interface Name {
first: string;
last: string;
}
class Person {
name: Name;
age: number;
}
var bob: Person = {
name: {
first: "Bob",
last: "Smith",
},
age: 35,
};
答案 2 :(得分:15)
您可以影响在类类型中输入的匿名对象。 奖金:在视觉工作室中,您通过这种方式受益于智能感知:)
var anInstance: AClass = <AClass> {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
编辑:
警告如果类有方法,则类的实例将无法获取它们。如果AClass有一个构造函数,它将不会被执行。如果您使用instanceof AClass,您将得到错误。
总之,你应该使用interface而不是class 。 最常见的用途是声明为Plain Old Objects的域模型。 实际上,对于域模型,您应该更好地使用接口而不是类。接口在编译时用于类型检查,与类不同,接口在编译期间被完全删除。
interface IModel {
Property1: string;
Property2: string;
PropertyBoolean: boolean;
PropertyNumber: number;
}
var anObject: IModel = {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
答案 3 :(得分:14)
以下是一种解决方案,它结合了Object.assign
的较短应用,以更接近地模拟原始C#
模式。
但首先,让我们回顾一下到目前为止提供的技术,其中包括:
Object.assign
Partial<T>
技巧Object.create
代替Object.assign
当然,每个人都有自己的优缺点。修改目标类以创建复制构造函数可能并不总是一个选项。并且&#34;铸造&#34;丢失与目标类型相关的任何功能。 Object.create
似乎不太吸引人,因为它需要一个相当冗长的属性描述符映射。
所以,这是另一种更简单的方法,维护类型定义和相关的函数原型,并更接近地模拟预期的C#
模式:
const john = Object.assign( new Person(), {
name: "John",
age: 29,
address: "Earth"
});
那就是它。 C#
模式的唯一添加是Object.assign
以及2个括号和逗号。查看下面的工作示例,确认它保留了类型的函数原型。不需要构造函数,也没有聪明的技巧。
此示例显示如何使用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() );
&#13;
答案 4 :(得分:12)
我建议一种不需要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();
关键点:
Partial<T>
答案 5 :(得分:11)
我更倾向于这样做,使用(可选)自动属性和默认值。您没有建议这两个字段是数据结构的一部分,所以这就是我选择这种方式的原因。
您可以拥有该类的属性,然后以通常的方式分配它们。显然它们可能需要也可能不需要,所以这也是其他的东西。只是这是一个很好的语法糖。
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
答案 6 :(得分:10)
在某些情况下,可以使用 Object.create
。如果您需要反向兼容性或想要滚动自己的初始化函数,Mozilla参考包括polyfill。
应用于您的示例:
Object.create(Person.prototype, {
'Field1': { value: 'ASD' },
'Field2': { value: 'QWE' }
});
在我的情况下,我发现这在单元测试中很有用,原因有两个:
__proto__
)并且不通过测试。例如:var actual = new MyClass();
actual.field1 = "ASD";
expect({ field1: "ASD" }).toEqual(actual); // fails
单元测试失败的输出不会产生关于不匹配的线索。
最后,http://typescript.codeplex.com/workitem/334提出的解决方案不支持内联json样式声明。例如,以下内容无法编译:
var o = {
m: MyClass: { Field1:"ASD" }
};
答案 7 :(得分:2)
我想要一个具有以下优点的解决方案:
这是我的操作方式:
export class Person {
id!: number;
firstName!: string;
lastName!: string;
getFullName() {
return `${this.firstName} ${this.lastName}`;
}
constructor(data: OnlyData<Person>) {
Object.assign(this, data);
}
}
const person = new Person({ id: 5, firstName: "John", lastName: "Doe" });
person.getFullName();
构造函数中的所有属性都是必需的,没有编译器错误就不能省略。
依赖于OnlyData
过滤掉getFullName()
所需的属性,其定义如下:
// based on : https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
type FilterFlags<Base, Condition> = { [Key in keyof Base]: Base[Key] extends Condition ? never : Key };
type AllowedNames<Base, Condition> = FilterFlags<Base, Condition>[keyof Base];
type SubType<Base, Condition> = Pick<Base, AllowedNames<Base, Condition>>;
type OnlyData<T> = SubType<T, (_: any) => any>;
当前这种方式的局限性:
答案 8 :(得分:2)
您可能有一个带有可选字段的类(标有?),并且有一个接收相同类实例的构造函数。
class Person {
name: string; // required
address?: string; // optional
age?: number; // optional
constructor(person: Person) {
Object.assign(this, person);
}
}
let persons = [
new Person({ name: "John" }),
new Person({ address: "Earth" }),
new Person({ age: 20, address: "Earth", name: "John" }),
];
在这种情况下,您将无法省略必填字段。这使您可以对对象的构造进行细粒度的控制。
您可以将构造函数与Partial类型一起使用,如其他答案所述:
public constructor(init?:Partial<Person>) {
Object.assign(this, init);
}
问题在于所有字段都变为可选字段,并且在大多数情况下是不希望的。
答案 9 :(得分:2)
要初始化一个类而不重新声明所有默认属性:
class MyClass{
prop1!: string //required to be passed in
prop2!: string //required to be passed in
prop3 = 'some default'
prop4 = 123
constructor(opts:{prop1:string, prop2:string} & Partial<MyClass>){
Object.assign(this,opts)
}
}
这结合了一些已经很好的答案
答案 10 :(得分:1)
最简单的方法是使用类型转换。
return <MyClass>{ Field1: "ASD", Field2: "QWE" };
答案 11 :(得分:1)
这是一个解决方案:
Partial<...>
不同)OnlyData<...>
解决方案不同)唯一的缺点是它一开始看起来更复杂。
// Define all fields here
interface PersonParams {
id: string
name?: string
coolCallback: () => string
}
// extend the params interface with an interface that has
// the same class name as the target class
// (if you omit the Params interface, you will have to redeclare
// all variables in the Person class)
interface Person extends PersonParams { }
// merge the Person interface with Person class (no need to repeat params)
// person will have all fields of PersonParams
// (yes, this is valid TS)
class Person {
constructor(params: PersonParams) {
// could also do Object.assign(this, params);
this.id = params.id;
this.name = params.name;
// intellisence will expect params
// to have `coolCallback` but not `sayHello`
this.coolCallback = params.coolCallback;
}
// compatible with functions
sayHello() {
console.log(`Hi ${this.name}!`);
}
}
// you can only export on another line (not `export default class...`)
export default Person;
答案 12 :(得分:0)
这是我为此找到的最佳解决方案。
声明一个可以用作装饰器的函数。我称之为自动反射
export function AutoReflect<T extends { new(...args: any[]): {} }>(
constructor: T
) {
return class extends constructor {
constructor(...args: any[]) {
super(args)
if (typeof args[0] === 'object') {
Object.assign(this, args[0]);
}
}
};
}
它所做的是期望构造函数中有一个对象并将成员分配给类实例。 在类声明中使用它
interface IPerson {
name: string;
age: number;
}
@AutoReflect
class Person implements IPerson {
name: string;
number: number;
constructor(model?: Partial<IPerson>){}
}
在模型的构造函数中,您可以将模型设为可选,而在使用 Partial 时,您可以在不设置所有属性值的情况下新建一个实例
new Person({
name: 'Santa'
});
这个方法创建了一个你想要的类的新实例,并且还有一个 C# 对象初始化的感觉。
答案 13 :(得分:0)
对于更现代的 TypeScript 版本
类定义
export class PaymentRequestDto {
public PaymentSource: number;
public PaymentCenterUid: string;
public ConnectedUserUid: string;
}
你有一些来自某处的价值观:
const PaymentCenter= 'EA0AC01E-D34E-493B-92FF-EB2D66512345';
const PaymentSource= 4;
const ConnectedUser= '2AB0D13C-2BBE-46F5-990D-533067BE2EB3';
然后你可以在强类型的同时初始化你的对象。
const parameters: PaymentRequestDto = {
PaymentSource,
PaymentCenterUid: PaymentCenter,
ConnectedUserUid: ConnectedUser,
};
PaymentSource 不需要名称字段说明符,因为使用的变量与字段名称相同。
这也适用于数组。
const parameters: PaymentRequestDto [] = [
{
PaymentSource,
PaymentCenterUid: PaymentCenter,
ConnectedUserUid: ConnectedUser,
},
{
. . . .
}
];
答案 14 :(得分:0)
这是另一种解决方案:
return {
Field1 : "ASD",
Field2 : "QWE"
} as myClass;
答案 15 :(得分:0)
如果要创建实例时不设置初始值的新实例
1-您必须使用类而非接口
2-创建类时,您必须设置初始值
export class IStudentDTO {
Id: number = 0;
Name: string = '';
student: IStudentDTO = new IStudentDTO();
答案 16 :(得分:-1)
如果您使用旧版本的打字稿&lt; 2.1然后你可以使用类似于以下内容,基本上是任何类型对象的转换:
const typedProduct = <Product>{
code: <string>product.sku
};
注意:使用此方法仅适用于要删除的数据模型 对象中的所有方法。它基本上是将任何对象转换为a 打字对象