如何init一个新的类在TS以这样的方式(在c#的例子,以显示我想要的):
// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ... some code after
如何init一个新的类在TS以这样的方式(在c#的例子,以显示我想要的):
// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ... some code after
当前回答
这里有一个解决方案:
不强迫你让所有字段都是可选的(不像Partial<…>) 区分类方法和函数类型的字段(不同于OnlyData<…>解决方案) 通过定义Params接口提供了一个很好的结构 不需要重复变量名和类型不止一次
唯一的缺点是一开始看起来比较复杂。
// 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;
其他回答
type ExcludeMethods<T> = Pick<T, { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]>;
class Person{
name: string = "N/A";
age: number = 0;
gender?: "male" | "female"
constructor(init?:ExcludeMethods<Person>){
Object.assign(this, init);
}
Describe(){return `${this.name} ${this.age} ${this.gender ?? ""}` }
}
var p1 = new Person();
var p2 = new Person({
name: "John",
age: 20
});
var p3 = new Person({
name: "Mary",
age: 25,
gender: "female"
});
console.log(p1.Describe()) // N/A 0
console.log(p2.Describe()) // John 20
console.log(p3.Describe()) // Mary 25 female
我想要一个解决方案,将有以下:
所有数据对象都是必需的,并且必须由构造函数填充。 不需要提供默认值。 可以在类内部使用函数。
我是这样做的:
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>;
目前这种方式的局限性:
需要TypeScript 2.8 具有getter /setter的类
在某些情况下,使用Object.create可能是可以接受的。如果您需要向后兼容或想要滚动自己的初始化函数,Mozilla引用包含一个polyfill。
应用于你的例子:
Object.create(Person.prototype, {
'Field1': { value: 'ASD' },
'Field2': { value: 'QWE' }
});
有用的场景
单元测试 内联声明
在我的案例中,我发现这在单元测试中很有用,原因有二:
在测试期望时,我经常希望创建一个苗条的对象作为期望 单元测试框架(如Jasmine)可能会比较对象原型(__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" }
};
可以有一个带有可选字段(用?标记)的类和一个接收同一类实例的构造函数。
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);
}
问题是所有字段都是可选的,在大多数情况下都不可取。
我更倾向于这样做,使用(可选的)自动属性和默认值。您没有建议这两个字段是数据结构的一部分,所以这就是我选择这种方式的原因。
您可以在类中拥有属性,然后以通常的方式分配它们。显然,他们可能需要,也可能不需要,所以这也是另一回事。只是这是一个很好的语法糖。
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