如何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
当前回答
可以有一个带有可选字段(用?标记)的类和一个接收同一类实例的构造函数。
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);
}
问题是所有字段都是可选的,在大多数情况下都不可取。
其他回答
下面是一个结合了较短的Object应用程序的解决方案。赋值来更紧密地模拟原始的c#模式。
但首先,让我们回顾一下到目前为止提供的技术,包括:
复制接受对象的构造函数,并将其应用于object .assign 复制构造函数中的一个聪明的Partial<T>技巧 针对POJO使用“强制转换” 利用对象。create而不是Object.assign
当然,每种方法都有其优点和缺点。修改目标类以创建复制构造函数可能并不总是一种选择。而“强制转换”会丢失与目标类型相关的所有函数。对象。Create似乎不那么吸引人,因为它需要相当冗长的属性描述符映射。
简短、通用的回答
因此,这里还有另一种更简单的方法,它维护了类型定义和相关的函数原型,并更紧密地模拟了预期的c#模式:
const john = Object.assign( new Person(), {
name: "John",
age: 29,
address: "Earth"
});
就是这样。在c#模式上唯一增加的是Object。赋值时加上2个括号和一个逗号。查看下面的工作示例,确认它维护了类型的函数原型。不需要构造函数,也不需要巧妙的技巧。
工作示例
这个例子展示了如何使用近似c#字段初始化器来初始化一个对象:
人物{ 名称:string = "; 地址:string = "; 年龄:0; aboutMe () { 返回' Hi, I'm ${this.name}, age ${this.name。年龄}和来自${this.address} '; } } // typescript字段初始化式(维护"type"定义) const john =对象。assign(new Person(), { 名称:“约翰”, 年龄:29岁 地址:“地球” }); //初始化对象维护aboutMe()函数原型 console.log(john.aboutMe());
可以影响类类型中强制转换的匿名对象。 奖励:在visual studio中,你可以这样受益于智能感知:)
var anInstance: AClass = <AClass> {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
编辑:
警告:如果类有方法,类的实例将得不到它们。如果AClass有构造函数,它将不会被执行。如果使用instanceof AClass,则会得到false。
总之,应该使用接口而不是类。 最常见的用途是声明为普通旧对象的域模型。 实际上,对于域模型,您应该更好地使用接口而不是类。接口在编译时用于类型检查,与类不同,接口在编译期间被完全删除。
interface IModel {
Property1: string;
Property2: string;
PropertyBoolean: boolean;
PropertyNumber: number;
}
var anObject: IModel = {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
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
这里有一个解决方案:
不强迫你让所有字段都是可选的(不像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;
最简单的方法是使用类型强制转换。
return <MyClass>{ Field1: "ASD", Field2: "QWE" };