332

そのような方法でTSで新しいクラスを初期化する方法(私が欲しいものを示すためのC#の例):

// ... some code before
return new MyClass { Field1 = "ASD", Field2 = "QWE" };
// ...  some code after
4

17 に答える 17

571

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構文は拒否されました)。適切なフィールドイニシャライザーの方がはるかに望ましいのですが、まだ実現するようには見えません。

比較のために、キャストアプローチを使用する場合、初期化オブジェクトには、キャスト先のタイプのすべてのフィールドが含まれている必要があります。さらに、クラス自体によって作成されたクラス固有の関数(または派生)を取得しないでください。

于 2016-06-07T14:38:35.030 に答える
111

アップデート

この答えを書いて以来、より良い方法が出てきました。より多くの票とより良い答えを持っている以下の他の答えを見てください。承認済みとしてマークされているため、この回答を削除できません。


古い答え

これを説明するTypeScriptコードプレックスに問題があります:オブジェクト初期化子のサポート

すでに述べたように、クラスの代わりにTypeScriptのインターフェースを使用することで、これをすでに行うことができます。

interface Name {
    givenName: string;
    surname: string;
}
class Person {
    name: Name;
    age: number;
}

var bob: Person = {
    name: {
        givenName: "Bob",
        surname: "Smith",
    },
    age: 35,
};
于 2013-01-03T15:44:29.953 に答える
46

以下は、の短いアプリケーションを組み合わせて、元のパターンObject.assignをより厳密にモデル化するソリューションです。C#

ただし、最初に、これまでに提供された手法を確認しましょう。これには次のものが含まれます。

  1. オブジェクトを受け入れるコンストラクターをコピーし、それをに適用しますObject.assign
  2. コピーコンストラクター内の巧妙なPartial<T>トリック
  3. POJOに対する「キャスティング」の使用
  4. 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() );

于 2017-10-21T03:38:24.850 に答える
32

クラスタイプでキャストされた匿名オブジェクトに影響を与えることができます。 ボーナス:ビジュアルスタジオでは、この方法でインテリセンスのメリットがあります:)

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
 };
于 2015-11-17T08:17:48.433 に答える
18

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();

キーポイント:

  • Typescript 2.1は不要、Partial<T>不要
  • 関数をサポートします(関数をサポートしない単純型アサーションと比較して)
于 2017-03-06T05:59:00.800 に答える
15

シナリオによっては、を使用しても問題ない場合がありますObject.create。下位互換性が必要な場合、または独自の初期化関数をロールしたい場合は、Mozillaリファレンスにポリフィルが含まれています。

あなたの例に適用:

Object.create(Person.prototype, {
    'Field1': { value: 'ASD' },
    'Field2': { value: 'QWE' }
});

便利なシナリオ

  • ユニットテスト
  • インライン宣言

私の場合、これは2つの理由で単体テストで役立つことがわかりました。

  1. 期待をテストするとき、私はしばしば期待としてスリムなオブジェクトを作成したいと思います
  2. ユニットテストフレームワーク(Jasmineなど)は、オブジェクトのプロトタイプ(__proto__)を比較して、テストに失敗する場合があります。例えば:
var actual = new MyClass();
actual.field1 = "ASD";
expect({ field1: "ASD" }).toEqual(actual); // fails

単体テストの失敗の出力は、何が不一致であるかについての手がかりを生み出しません。

  1. 単体テストでは、サポートするブラウザーを選択できます

最後に、http://typescript.codeplex.com/workitem/334で提案されているソリューションは、インラインjsonスタイルの宣言をサポートしていません。たとえば、以下はコンパイルされません。

var o = { 
  m: MyClass: { Field1:"ASD" }
};
于 2015-08-11T20:52:09.023 に答える
13

オプションのフィールド(?でマークされている)を持つクラスと、同じクラスのインスタンスを受け取るコンストラクターを持つことができます。

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);
}

問題は、すべてのフィールドがオプションになり、ほとんどの場合望ましくないことです。

于 2019-10-26T16:14:36.043 に答える
12

(オプションで)自動プロパティとデフォルトを使用して、この方法でそれを行う傾向があります。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
于 2013-01-09T01:15:15.453 に答える
12

次のようなソリューションが必要でした。

  • すべてのデータオブジェクトは必須であり、コンストラクターによって入力される必要があります。
  • デフォルトを提供する必要はありません。
  • クラス内で関数を使用できます。

これが私がそれをする方法です:

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>;

この方法の現在の制限:

  • TypeScript2.8が必要
  • ゲッター/セッターのいるクラス
于 2019-04-23T18:51:01.097 に答える
4

これは別の解決策です:

return {
  Field1 : "ASD",
  Field2 : "QWE" 
} as myClass;
于 2019-12-04T13:09:37.570 に答える
4

デフォルトのすべてのプロパティを再宣言せずにクラスを初期化するには、次のようにします。

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)
  }
}

これは、すでに優れた答えのいくつかを組み合わせたものです

于 2020-07-12T06:05:34.990 に答える
3

これが私がこれに対して見つけた最良の解決策です。

デコレータとして使用できる関数を宣言します。私はそれを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#オブジェクトの初期化感覚も備えています。

于 2021-05-18T21:01:18.870 に答える
2

これを行う最も簡単な方法は、型キャストを使用することです。

return <MyClass>{ Field1: "ASD", Field2: "QWE" };
于 2016-08-07T04:05:45.000 に答える
1

古いバージョンのtypescript<2.1を使用している場合は、基本的に任意のオブジェクトを型指定されたオブジェクトにキャストする次のようなものを使用できます。

const typedProduct = <Product>{
                    code: <string>product.sku
                };

注:このメソッドを使用すると、オブジェクト内のすべてのメソッドが削除されるため、データモデルにのみ適しています。基本的に、任意のオブジェクトを型指定されたオブジェクトにキャストします

于 2018-06-01T10:34:58.283 に答える
1

これが解決策です:

  • すべてのフィールドをオプションにすることを強制しません(とは異なります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;
于 2021-01-24T19:46:36.220 に答える
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,
      },
      {
      . . . .
      }
    ];
于 2021-04-22T20:37:37.230 に答える
-2

インスタンス時に初期値を設定せずに新しいインスタンスを作成する場合

1-インターフェースではなくクラスを使用する必要があります

2-クラスを作成するときに初期値を設定する必要があります

export class IStudentDTO {
 Id:        number = 0;
 Name:      string = '';


student: IStudentDTO = new IStudentDTO();
于 2019-12-03T12:33:27.357 に答える