3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[TypeScript] Omit を使って interface のフィールドを再定義する

Last updated at Posted at 2023-04-30

はじめに

すでに定義されているインターフェースのフィールドはそのままに、特定のフィールドの型のみ変更したいケースがありました。
本記事では以下の 2ケース を扱い、その実現方法について提示します。

  • ケース1: フラットなインターフェース
  • ケース2: 入れ子となったインターフェース

前提

フィールドの型がすべて string のインターフェース BaseModel があります。
この BaseModel が本記事で扱うインターフェースのすべての基点です。

interface BaseModel {
    'a': string;
    'b': string;
    'c': string;
    'd': string;
}

ケース-1: フラットなインターフェース

インターフェース BaseModel に対して a, b のフィールドの型を number に変更したいです。
しかしながら、この BaseModel は複数の箇所で使われている可能性もあり、直接この BaseModel に対してフィールドの型を変更するのはリスキーです。

そのまま継承して型を変更することはできない

かといって、BaseModel を継承してフィールド a,b の値をnumber にしてもエラーが発生します。
以下の定義は a, bnumber で再定義しようとしていますが、TS2430 が発生します。

// 単純な再定義では TS2430 が発生する
interface ExtendBaseModelA extends BaseModel {
    'a': number;
    'b': number;
}

Omit を使って対応する

こんなときは Omit を使ってフィールドを削除 し、その上で 型を再定義したインターフェースを作る ことで実現できます。

// Omit を使って対象のフィールドを削除した後に再定義する
type OmitField = 'a' | 'b';
interface ExtendBaseModelB extends Omit<BaseModel, OmitField> {
    // 'a', 'b' を number で定義しても TS2430 は発生しない
    'a': number;
    'b': number;
}

エラーの有無を検証する

BaseModel とそれを継承してフィールドの再定義を行った ExtendBaseModelB に対して値の設定を行い、エラーの有無を確認します。

BaseModel の検証

BaseModel のフィールドの型はすべて string です。よって各フィールドに文字列を指定すればエラーは発生しません。

// BaseModel の型をもつ baseModelA には全フィールドに対して string が設定できる
const baseModelA: BaseModel = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
};

ですが、string 以外の値を設定すると TS2322 が発生します。

// BaseModel の型をもつ baseModelB に string 以外の型を設定するとエラー(TS2322)となる
const baseModelB: BaseModel = {
    'a': 1, // TS2322 が発生する
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
}

ExtendBaseModelB の検証

BaseModel を継承して作ったインターフェース ExtendBaseModelBa, b のフィールドには number 型を指定しています。
したがって a, bnumber を指定してもエラーにはなりません。

// ExtendBaseModelB の型をもつ dxtendBaseModelB1 には 'a', 'b' には number が設定できる
const dxtendBaseModelB1: ExtendBaseModelB = {
    'a': 1,
    'b': 2,
    'c': 'CCCC',
    'd': 'DDDD',
};

逆に a, b に対して string を指定すると TS2322 が発生します。

// ExtendBaseModelB の型をもつ dxtendBaseModelB2 の 'a' や  'b' に string 以外の型を設定するとエラー(TS2322)となる
const dxtendBaseModelB2: ExtendBaseModelB = {
    'a': 1,
    'b': 'BBBB', // TS2322 が発生する
    'c': 'CCCC',
    'd': 'DDDD',
}

なお蛇足ですが、a, b のフィールドそのものを指定しない場合は TS2739 が発生します。
これは BaseModel を指定していても同じです。

// ExtendBaseModelB の型をもつ dxtendBaseModelB3 に 'a', 'b' を設定しないとエラー(TS2739)になる
const dxtendBaseModelB3: ExtendBaseModelB = {
    'c': 'CCCC',
    'd': 'DDDD',
}

ケース-2: 入れ子となったインターフェース

インターフェースはすべてのフィールドがフラットで定義されているとは限りません。
別のインターフェースを指定したフィールドが存在する、いわゆる 入れ子構造 になっているケースもあります。

例えばフィールドの型がすべて string である ChildBaseModel があります。

interface ChildBaseModel {
    'e': string;
    'f': string;
    'g': string;
}

そして BaseModel を継承した ExtendBaseModelC を定義し、型が ChildBaseModel である フィールド cb を持たせます。
この入れ子になったフィールド cb の型である ChildBaseModel が持つフィールドの再定義 を見ていきます。

interface ExtendBaseModelC extends BaseModel {
    cb: ChildBaseModel;
}

やることは一緒

とはいえ、入れ子となっていてもやることは一緒です。
入れ子部分である cb の フィールド g の型を number にしたいとき、 Omit を使って新しく型を作ってやります。

type OmitFieldCB = 'g';
interface ExtednChildModel extends Omit<ChildBaseModel, OmitFieldCB> {
    'g': number;
}

// ExtednChildModel を入れ子にしたモデルを定義する
interface ExtendBaseModelD extends BaseModel {
    cb: ExtednChildModel;
}

エラーの有無を検証する

入れ子となったフィールドに対して値の設定を行い、エラーの有無を確認します。

ExtendBaseModelC の検証

ExtendBaseModelC は入れ子のインターフェース含めてすべてのフィールドの型が string です。
以下はすべてのフィールドに対して文字列を設定しているのでエラーは発生しません。

const extendBaseModelC: ExtendBaseModelC = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
    cb: {
        'e': 'EEEE',
        'f': 'FFFF',
        'g': 'GGGG'
    }
}

次のコードでは入れ子である cb のフィールド g に対して数値を設定しています。
このとき cb[g] には TS2322 が発生します。これは文字列の定義に対して数値を設定したためです。

const extendBaseModelC: ExtendBaseModelC = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
    cb: {
        'e': 'EEEE',
        'f': 'FFFF',
        'g': 3 // TS2322 が発生する
    }
}

ExtendBaseModelD の検証

こんどは入れ子である cb の型が ExtednChildModel である ExtendBaseModelD について確認します。
ExtednChildModelD はフィールド g の型が number です。

以下は cb[g] を数値で設定してもエラーとなりません。

const extendBaseModelD: ExtendBaseModelD = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
    cb: {
        'e': 'EEEE',
        'f': 'FFFF',
        'g': 3
    }
}

ですが、次のように cb[g] に数値以外を設定すると TS2322 が発生します。

const extendBaseModelD: ExtendBaseModelD = {
    'a': 'AAAA',
    'b': 'BBBB',
    'c': 'CCCC',
    'd': 'DDDD',
    cb: {
        'e': 'EEEE',
        'f': 'FFFF',
        'g': 'GGGG' // cb['g'] に number 以外を設定したら TS2322 が発生する
    }
}

参考

3
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?