Typescriptインターフェースのデフォルト値


137

TypeScriptには次のインターフェイスがあります。

interface IX {
    a: string,
    b: any,
    c: AnotherType
}

そのタイプの変数を宣言し、すべてのプロパティを初期化します

let x: IX = {
    a: 'abc',
    b: null,
    c: null
}

次に、実際の値を後でinit関数で割り当てます

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

ただし、オブジェクトを後で実際の値に設定するときにオブジェクトを宣言するときに、各プロパティに一連のデフォルトのnull値を指定する必要はありません。提供しないプロパティをデフォルトでnullに設定するようにインターフェイスに指示できますか?何がこれを可能にするでしょう:

let x: IX = {
    a: 'abc'
}

コンパイラエラーが発生することはありません。今それは私に伝えます

TS2322:タイプ '{}'はタイプ 'IX'に割り当てることができません。タイプ '{}'にプロパティ 'b'がありません。


私はあなたのためにドキュメントを追加しました:basarat.gitbooks.io/typescript/content/docs/tips/...
— basarat

3
IMO、答えstackoverflow.com/a/35074490/129196を採用するべきではありません。すべてのプロパティを初期化せずにオブジェクトを状態にでき、それでも有効である場合は、これらのプロパティをオプションとして宣言する必要があります。この回答は、stackoverflow.com / a / 43226857/129196です。そうでなければ、タイプスクリプトを使用するという唯一の目的が失われます(タイプセーフのため)。
— Charles Prakash Dasari、2018

回答:


95

提供しないプロパティをデフォルトでnullに設定するようにインターフェイスに指示できますか?何ができるようになりますか

いいえ。ただし、デフォルトundefinedでは、ほとんど問題ありません。次のパターンを使用できます。つまり、作成時にタイプアサーションを使用できます。

let x: IX = {} as any;

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

これと他のパターンがここに文書化されています:https : //basarat.gitbook.io/typescript/main-1/lazyobjectliteralinitialization


たくさんのデフォルト値を定義せずに、未定義のプロパティに値を設定できなかった
— Omar Isaid

42
を使用anyすると、タイプスクリプトの目的が損なわれます。この欠点のない他の答えがあります。
— ジャックミラー、

誰でも私と同様の質問について私を助けることができますが、ジェネリックを使用しています。ここでこの質問
— TAB

2
提供されたリンクで、彼が 'let foo = {} as Foo;'ではるかに優れたオプションを提供している場合、バサラトは 'any'の例を使用します。( 'Foo'はインターフェースです)
— Neurothustra

1
OPはTypeScriptインターフェースの作成に悩んでおり、ボイラーメッキの解決策を求めています。あなたの解決策はインターフェースを完全に忘れることです?TypeScriptをスキップすることもお勧めします...
— Moss Palmer

98

インターフェイスでデフォルト値を設定することはできませんが、オプションのプロパティを使用して目的の操作を実行できます(段落#3を比較)。

https://www.typescriptlang.org/docs/handbook/interfaces.html

インターフェイスを次のように変更するだけです。

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}

その後、次のことができます。

let x: IX = {
    a: 'abc'
}

とに割り当てデフォルト値にあなたのinit関数を使用x.bし、x.cそれらのproperiesが設定されていない場合。


3
質問では、初期化x.bとx.cを使用するように求められましたnull。let x = {a: 'abc'}then x.bとx.careを書いているときundefined、この回答は要件を完全には満たしていませんが、スマートなクイックフィックスです。
— Benny Neugebauer

1
@BennyNeugebauer受け入れられた答えには同じ欠陥があります。これが最良の答えです
— 電話

1
xがデフォルト値のオブジェクトの場合、最終的なインスタンスオブジェクトlet a: IX = Object.assign({b:true}, x);を作成すると、インスタンスオブジェクトでもb、cがオプションになるため、望ましくない場合があります
— Luckylooke

プロパティは、インターフェースを使用するユーザーの便宜のために「オプション」とマークされるべきではありません。答えは「いいえ」であり、「デフォルトを提供するようにインターフェースに通知する」ことはできませんが、インターフェースのインスタンスを初期化するファクトリメソッドを提供できます。
— Rick O'Shea

34

@Timarの回答はnullデフォルト値(要求されたもの)に対して完全に機能しますが、他のデフォルト値を許可する別の簡単な解決策は次のとおりです。コンストラクターで、スプレッド演算子を使用してoptionsメンバー変数を設定します

interface IXOptions {
    a?: string,
    b?: any,
    c?: number
}

const XDefaults: IXOptions = {
    a: "default",
    b: null,
    c: 1
}

export class ClassX {
    private options: IXOptions;

    constructor(XOptions: IXOptions) {
        this.options = { ...XDefaults, ...XOptions };
    }

    public printOptions(): void {
        console.log(this.options.a);
        console.log(this.options.b);
        console.log(this.options.c);
    }
}

これで、次のようなクラスを使用できます。

const x = new ClassX({ a: "set" });
x.printOptions();

出力:

set
null
1

これはすべてのケースでデフォルトのソリューションとして使用することはできませんが、いくつかのケースでは間違いなく創造的なソリューションです。👍
— アブヘイtripathi

値をログに記録するだけでなく、const calcvalue = this.options.a * 1000; それは潜在的に未定義することができますので、これはまだ..アラームを引き上げる
— マニッシュRawat

はい、クラスを使用するか、別の言語でそれを書くことができますが、どちらも彼の質問には答えません。
— リックオシア

22

クラスを使用してインターフェースを実装すると、コンストラクターのメンバーの初期化を処理できます。

class IXClass implements IX {
    a: string;
    b: any;
    c: AnotherType;

    constructor(obj: IX);
    constructor(a: string, b: any, c: AnotherType);
    constructor() {
        if (arguments.length == 1) {
            this.a = arguments[0].a;
            this.b = arguments[0].b;
            this.c = arguments[0].c;
        } else {
            this.a = arguments[0];
            this.b = arguments[1];
            this.c = arguments[2];
        }
    }
}

別のアプローチは、ファクトリ関数を使用することです:

function ixFactory(a: string, b: any, c: AnotherType): IX {
    return {
        a: a,
        b: b,
        c: c
    }
}

次に、簡単にできます:

var ix: IX = null;
...

ix = new IXClass(...);
// or
ix = ixFactory(...);

4

Partialドキュメントで説明されているように、マップされたタイプを使用できます:https : //www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

あなたの例では、次のようになります:

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

let x: Partial<IX> = {
    a: 'abc'
}

xがデフォルト値のオブジェクトの場合、最終的なインスタンスオブジェクトlet a: IX = Object.assign({b:true}, x);を作成するとエラーが発生しますPartial<IX> cannot be assigned to IX
— Luckylooke

タイプを部分的に変更します。xは実装されなくなりましたIXが、の一部ですIX。Partialすべてのプロパティがオプションである可能性がある場所、たとえばオブジェクトインターフェイスの一部を渡して、定義されているフィールドのみを更新できる場所に適しています(undefinedこれは、のすべてのフィールドがPartial可能なフィールドではありません)。デフォルト値を持つフィールドを持つインターフェースの場合、let x: Partial<IX> = { /* non-default fields */ } as IX構文を使用して、デフォルト値を宣言する必要なしに、それらのタイプのインターフェースを実装するオブジェクトリテラルを宣言できます。
— Paul-Sebastian Manole

1

私が到達した方法よりも良い方法を探しているときに、私はこれにつまずきました。回答を読んで試してみると、他の回答は私には簡潔に感じられなかったので、私がしていることを投稿する価値があると思いました。新しいインターフェイスをセットアップするたびに、短いコードを書くだけでよいことが重要でした。私は解決しました...

カスタムの一般的なdeepCopy関数の使用:

deepCopy = <T extends {}>(input: any): T => {
  return JSON.parse(JSON.stringify(input));
};

インターフェースを定義する

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

...そして、別のconstでデフォルトを定義します。

const XDef : IX = {
    a: '',
    b: null,
    c: null,
};

次に、このように初期化します:

let x : IX = deepCopy(XDef);

それだけで十分です。

.. しかしながら ..

ルート要素をカスタムで初期化する場合は、カスタムのデフォルト値を受け入れるように、deepCopy関数を変更できます。関数は次のようになります。

deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
  return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
};

代わりに、次のように呼び出すことができます。

let x : IX = deepCopyAssign(XDef, { a:'customInitValue' } );

ディープコピーのその他の推奨される方法はすべて機能します。浅いコピーのみが必要な場合は、Object.assignで十分であり、ユーティリティdeepCopyまたはdeepCopyAssign関数の必要性はありません。

let x : IX = object.assign({}, XDef, { a:'customInitValue' });

既知の問題点

  • この見た目では深く割り当てられませんが、deepCopyAssign割り当ての前にタイプを反復およびチェックするように変更することはそれほど難しくありません。
  • 関数と参照は、解析/文字列化プロセスによって失われます。私は自分の仕事にそれらを必要としませんし、OPも必要としませんでした。
  • カスタム初期値は、実行時にIDEまたは型チェックによって示唆されません。

0

私の解決策:

入力の問題を修正するために、Object.assignのラッパーを作成しました。

export function assign<T>(...args: T[] | Partial<T>[]): T {
  return Object.assign.apply(Object, [{}, ...args]);
}

使用法:

env.base.ts

export interface EnvironmentValues {
export interface EnvironmentValues {
  isBrowser: boolean;
  apiURL: string;
}

export const enviromentBaseValues: Partial<EnvironmentValues> = {
  isBrowser: typeof window !== 'undefined',
};

export default enviromentBaseValues;

env.dev.ts

import { EnvironmentValues, enviromentBaseValues } from './env.base';
import { assign } from '../utilities';

export const enviromentDevValues: EnvironmentValues = assign<EnvironmentValues>(
  {
    apiURL: '/api',
  },
  enviromentBaseValues
);

export default enviromentDevValues;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.