Typescript d.tsファイルで定義されたインターフェースプロパティタイプのオーバーライド


112

*.d.tstypescriptで定義されているインターフェイスプロパティのタイプを変更する方法はありますか?

例:のインターフェースx.d.tsは次のように定義されます

interface A {
  property: number;
}

書き込み先のtypescriptファイルで変更したい

interface A {
  property: Object;
}

またはこれでもうまくいく

interface B extends A {
  property: Object;
}

このアプローチは機能しますか?私のシステムで試したところ、うまくいきませんでした。それが可能かどうかを確認したいだけですか?

回答:


50

既存のプロパティのタイプを変更することはできません。

プロパティを追加できます:

interface A {
    newProperty: any;
}

しかし、既存のタイプを変更する:

interface A {
    property: any;
}

エラーになります:

後続の変数宣言は同じ型でなければなりません。変数 'property'はタイプ 'number'でなければなりませんが、ここにはタイプ 'any'があります

もちろん、既存のインターフェースを拡張する独自のインターフェースを持つことができます。その場合、タイプを互換性のあるタイプにのみオーバーライドできます。次に例を示します。

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

ちなみに、Objectタイプとしての使用は避け、代わりにタイプを使用してくださいany

タイプドキュメントでanyそれは述べています:

anyタイプは、既存のJavaScriptを使用する強力な方法であり、コンパイル時にタイプチェックを徐々にオプトインおよびオプトアウトできます。他の言語と同様に、Objectが同様の役割を果たすことを期待するかもしれません。ただし、Object型の変数では、値を割り当てることしかできません。実際に存在するものであっても、任意のメソッドを呼び出すことはできません

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Typescript> = 1.1では、インターフェイスを拡張してメソッドのタイプを上書きするには、元のインターフェイスからすべてのメソッドを含める必要があります。そうしないと、タイプに互換性がないというエラーが発生します。github.com
Microsoft /

最初に上書きする値を省略してから再定義できます。@ ZSkycatが解決する値に答えられるようにできますか?
zeachco

Javaを「他の言語」と呼ぶことに反対の投票
wvdz

@wvdz私が反対投票を気にするということではありませんが、何について話しているのですか?誰もがjavaに言及しましたか?ページで「java」を検索すると、検索結果が1つしかなく、コメントに含まれます。
Nitzan Tomer

多分私は少し不機嫌だったかもしれませんが、Javaのように、あなたが「他の言語」と言うことができたときに、あなたが「他の言語」と言ったのは少しイライラしました。または、オブジェクトをユニバーサル基本クラスとして持つ他の多くの言語がありますか?私はC#を知っていますが、もちろんC#はJavaに強く触発されました。
wvdz

200

最初にフィールドをフィルタリングしてからそれらを結合する方法を使用します。

参照は、タイプからプロパティを除外する

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

インターフェース:

interface A {
    x: string
}

interface B extends Omit<A, 'x'> {
  x: number
}

2
これを知るのは素晴らしいことです。しかし問題は、それがまだ既存のものを変更していないことです。
Freewind 2018年

8
これはまさに私が探していたものでした。それは私がtypescriptですが期待方法ですextendデフォルトでは仕事に、悲しいかな、この小さなOmit修正のすべて🙌
ドーソンB

1
インターフェースの拡張はまさに私が探していたものでした、ありがとう!
モホッジ

1
注:これを使用するには、上記のtypescript 3.5.3が必要です。
Vixson

89
type ModifiedType = Modify<OriginalType, {
  a: number;
  b: number;
}>

interface ModifiedInterface extends Modify<OriginalType, {
  a: number;
  b: number;
}> {}

ZSkycatの extends Omitソリューションに触発されて、私はこれを思いつきました:

type Modify<T, R> = Omit<T, keyof R> & R;

// before typescript@3.5
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

例:

interface OriginalInterface {
  a: string;
  b: boolean;
  c: number;
}

type ModifiedType  = Modify<OriginalInterface , {
  a: number;
  b: number;
}>

// ModifiedType = { a: number; b: number; c: number; }

ステップバイステップ:

type R0 = Omit<OriginalType, 'a' | 'b'>        // { c: number; }
type R1 = R0 & {a: number, b: number }         // { a: number; b: number; c: number; }

type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
type T1 = Pick<OriginalType, T0>               // { c: number; }
type T2 = T1 & {a: number, b: number }         // { a: number; b: number; c: number; }

TypeScriptユーティリティタイプ


8
これは素晴らしいソリューションです。
オースティンBrunkhorst

1
ここではNoobですが、あなたの例ではインターフェースからタイプに変更していますか?または違いはありませんか?
ドミニク

Niceeeeeeeeee:D
SaMiCoOo

1
@Dominic良い点、私は答えを更新しました。同じ名前の2つのインターフェースをマージできます。typescriptlang.org/docs/handbook/...
QWERTY配列

33

@zSkycatの答えを少し拡張すると、2つのオブジェクト型を受け入れ、2番目のメンバーが最初のメンバーをオーバーライドするマージされた型を返すジェネリックを作成できます。

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;

interface A {
    name: string;
    color?: string;
}

// redefine name to be string | number
type B = Merge<A, {
    name: string | number;
    favorite?: boolean;
}>;

let one: A = {
    name: 'asdf',
    color: 'blue'
};

// A can become B because the types are all compatible
let two: B = one;

let three: B = {
    name: 1
};

three.name = 'Bee';
three.favorite = true;
three.color = 'green';

// B cannot become A because the type of name (string | number) isn't compatible
// with A even though the value is a string
// Error: Type {...} is not assignable to type A
let four: A = three;

1
非常にクール:-)以前にOmitで1つまたは2つのプロパティを使用してこれを実行しましたが、これははるかにクールです:-)サーバーエンティティタイプを「拡張」して、クライアントで必須またはオプションになるように変更したいことがよくあります。
Simon_Weaver

1
これは今では受け入れられる解決策になるはずです。インターフェイスを「拡張」する最もクリーンな方法。
manuhortet

7

Omit インターフェース拡張時のプロパティ:

interface A {
  a: number;
  b: number;
}

interface B extends Omit<A, 'a'> {
  a: boolean;
}

3

面白いことに、同じケースを解決する可能性を調査している1日を過ごしています。私はこのようにすることは不可能であることがわかりました:

// a.ts - module
export interface A {
    x: string | any;
}

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

interface B extends A {
    x: SomeOtherType;
}

原因モジュールは、アプリケーションで使用可能なすべてのタイプを認識していない場合があります。そして、どこからでもすべてを移植してこのようなコードを実行するのはかなり退屈です。

export interface A {
    x: A | B | C | D ... Million Types Later
}

オートコンプリートを適切に機能させるには、後でタイプを定義する必要があります。


だからあなたは少しカンニングすることができます:

// a.ts - module
export interface A {
    x: string;
}

オーバーライドが不要な場合に、オートコンプリートが機能するいくつかのタイプをデフォルトで残しました。

その後

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

// @ts-ignore
interface B extends A {
    x: SomeOtherType;
}

@ts-ignoreフラグを使用してここで愚かな例外を無効にし、私たちが何か間違ったことをしていると言ってください。そして面白いことすべてが期待どおりに機能します。

私の場合、typeのスコープビジョンを減らしxています。これにより、コードをより厳密に行うことができます。たとえば、100個のプロパティのリストがある場合、それを10個に減らして、愚かな状況を回避します。


2

プロパティのタイプを絞り込むためにextendニッツァンの答えのように単純な方法は完璧に機能します

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

型を広げる、または一般的にオーバーライドするには、Zskycatのソリューションを実行できます。

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

ただし、インターフェースAが一般的なインターフェースを拡張している場合、を使用すると、Aの残りのプロパティのカスタムタイプが失われます。Omit

例えば

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = Omit<A, 'x'> & { x: number };

let b: B = { x: 2, y: "hi" }; // no error on b.y! 

その理由は、Omit内部的にはExclude<keyof A, 'x'>キーのみを通過するためですstring | number。したがって、Bになり{x: number; }、タイプがの追加プロパティを受け入れますnumber | string | boolean


これを修正するためにOverrideProps、次のような別のユーティリティタイプを思いつきました。

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

例:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = OverrideProps<A, { x: number }>;

let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!

1

これを行うために他の誰かが汎用ユーティリティタイプを必要とする場合、私は次の解決策を考え出しました。

/**
 * Returns object T, but with T[K] overridden to type U.
 * @example
 * type MyObject = { a: number, b: string }
 * OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
 */
export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };

私の場合、オーバーライドするキーはジェネリックそのものだったので、これが必要でした。

Omit準備ができていない場合は、タイプからプロパティ除外を参照してください。


1
これはまさに私が探していたものです。私はあなたに十分に感謝することはできません:D:D:D
dwoodwardgb

@dwoodwardgbそれが他の誰かにとって役に立てて嬉しい:-)
トニ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.