そのような方法でTSで新しいクラスを初期化する方法(私が欲しいものを示すためのC#の例):
// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ... some code after
そのような方法でTSで新しいクラスを初期化する方法(私が欲しいものを示すためのC#の例):
// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ... some code after
2016年7月12日更新:
Typescript 2.1では、マップされたタイプが導入され、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#スタイルにほぼ近いと思います(実際のfield-init構文は拒否されました)。適切なフィールドイニシャライザーの方がはるかに望ましいのですが、まだ実現するようには見えません。
比較のために、キャストアプローチを使用する場合、初期化オブジェクトには、キャスト先のタイプのすべてのフィールドが含まれている必要があります。さらに、クラス自体によって作成されたクラス固有の関数(または派生)を取得しないでください。
この答えを書いて以来、より良い方法が出てきました。より多くの票とより良い答えを持っている以下の他の答えを見てください。承認済みとしてマークされているため、この回答を削除できません。
これを説明するTypeScriptコードプレックスに問題があります:オブジェクト初期化子のサポート。
すでに述べたように、クラスの代わりにTypeScriptのインターフェースを使用することで、これをすでに行うことができます。
interface Name {
givenName: string;
surname: string;
}
class Person {
name: Name;
age: number;
}
var bob: Person = {
name: {
givenName: "Bob",
surname: "Smith",
},
age: 35,
};
以下は、の短いアプリケーションを組み合わせて、元のパターンObject.assign
をより厳密にモデル化するソリューションです。C#
ただし、最初に、これまでに提供された手法を確認しましょう。これには次のものが含まれます。
Object.assign
Partial<T>
トリックObject.create
代わりにレバレッジObject.assign
もちろん、それぞれに長所と短所があります。コピーコンストラクターを作成するためにターゲットクラスを変更することは、常にオプションであるとは限りません。また、「キャスト」は、ターゲットタイプに関連付けられている機能をすべて失います。 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() );
クラスタイプでキャストされた匿名オブジェクトに影響を与えることができます。 ボーナス:ビジュアルスタジオでは、この方法でインテリセンスのメリットがあります:)
var anInstance: AClass = <AClass> {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
編集:
警告クラスにメソッドがある場合、クラスのインスタンスはそれらを取得しません。AClassにコンストラクターがある場合、それは実行されません。instanceof AClassを使用すると、falseになります。
結論として、クラスではなくインターフェースを使用する必要があります。最も一般的な使用法は、PlainOldObjectsとして宣言されたドメインモデルです。実際、ドメインモデルの場合は、クラスではなくインターフェイスを使用する方が適切です。インターフェイスはコンパイル時に型チェックに使用され、クラスとは異なり、インターフェイスはコンパイル中に完全に削除されます。
interface IModel {
Property1: string;
Property2: string;
PropertyBoolean: boolean;
PropertyNumber: number;
}
var anObject: IModel = {
Property1: "Value",
Property2: "Value",
PropertyBoolean: true,
PropertyNumber: 1
};
Typescript2.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>
不要シナリオによっては、を使用しても問題ない場合がありますObject.create
。下位互換性が必要な場合、または独自の初期化関数をロールしたい場合は、Mozillaリファレンスにポリフィルが含まれています。
あなたの例に適用:
Object.create(Person.prototype, {
'Field1': { value: 'ASD' },
'Field2': { value: 'QWE' }
});
私の場合、これは2つの理由で単体テストで役立つことがわかりました。
__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);
}
問題は、すべてのフィールドがオプションになり、ほとんどの場合望ましくないことです。
(オプションで)自動プロパティとデフォルトを使用して、この方法でそれを行う傾向があります。2つのフィールドがデータ構造の一部であることを示唆していないので、この方法を選択しました。
クラスにプロパティを設定してから、通常の方法でそれらを割り当てることができます。そして明らかにそれらは必要かもしれないし必要でないかもしれないので、それもまた別のことです。これがとても素晴らしい構文糖衣であるというだけです。
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
次のようなソリューションが必要でした。
これが私がそれをする方法です:
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>;
この方法の現在の制限:
これは別の解決策です:
return {
Field1 : "ASD",
Field2 : "QWE"
} as myClass;
デフォルトのすべてのプロパティを再宣言せずにクラスを初期化するには、次のようにします。
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)
}
}
これは、すでに優れた答えのいくつかを組み合わせたものです
これが私がこれに対して見つけた最良の解決策です。
デコレータとして使用できる関数を宣言します。私はそれをAutoReflectと呼んでいます
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#オブジェクトの初期化感覚も備えています。
これを行う最も簡単な方法は、型キャストを使用することです。
return <MyClass>{ Field1: "ASD", Field2: "QWE" };
古いバージョンのtypescript<2.1を使用している場合は、基本的に任意のオブジェクトを型指定されたオブジェクトにキャストする次のようなものを使用できます。
const typedProduct = <Product>{
code: <string>product.sku
};
注:このメソッドを使用すると、オブジェクト内のすべてのメソッドが削除されるため、データモデルにのみ適しています。基本的に、任意のオブジェクトを型指定されたオブジェクトにキャストします
これが解決策です:
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;
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,
},
{
. . . .
}
];
インスタンス時に初期値を設定せずに新しいインスタンスを作成する場合
1-インターフェースではなくクラスを使用する必要があります
2-クラスを作成するときに初期値を設定する必要があります
export class IStudentDTO {
Id: number = 0;
Name: string = '';
student: IStudentDTO = new IStudentDTO();