Überschreiben des in der Datei Typescript d.ts definierten Eigenschaftstyps der Schnittstelle


106

Gibt es eine Möglichkeit, den Typ der in einem *.d.tsIn-Typoskript definierten Schnittstelleneigenschaft zu ändern ?

Beispiel: Eine Schnittstelle in x.d.tsist definiert als

interface A {
  property: number;
}

Ich möchte es in den Typoskriptdateien ändern, in die ich schreibe

interface A {
  property: Object;
}

oder sogar das würde funktionieren

interface B extends A {
  property: Object;
}

Wird dieser Ansatz funktionieren? Es hat nicht funktioniert, als ich mein System ausprobiert habe. Möchten Sie nur bestätigen, ob es überhaupt möglich ist?

Antworten:


48

Sie können den Typ einer vorhandenen Eigenschaft nicht ändern.

Sie können eine Eigenschaft hinzufügen:

interface A {
    newProperty: any;
}

Aber eine Art von vorhandenem ändern:

interface A {
    property: any;
}

Führt zu einem Fehler:

Nachfolgende Variablendeklarationen müssen denselben Typ haben. Die Variable 'property' muss vom Typ 'number' sein, hat hier jedoch den Typ 'any'.

Sie können natürlich eine eigene Schnittstelle haben, die eine vorhandene erweitert. In diesem Fall können Sie einen Typ nur auf einen kompatiblen Typ überschreiben, z. B.:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Übrigens sollten Sie die Verwendung Objectals Typ wahrscheinlich vermeiden , stattdessen den Typ verwenden any.

In den Dokumenten für den anyTyp heißt es:

Jeder Typ ist eine leistungsstarke Methode, um mit vorhandenem JavaScript zu arbeiten. So können Sie die Typprüfung während der Kompilierung schrittweise aktivieren und deaktivieren. Sie können erwarten, dass Object eine ähnliche Rolle spielt wie in anderen Sprachen. Mit Variablen vom Typ Object können Sie ihnen jedoch nur einen beliebigen Wert zuweisen. Sie können keine beliebigen Methoden für sie aufrufen, auch keine tatsächlich vorhandenen :

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'.

Mit Typescript> = 1.1, um den Typ der Methoden durch Erweitern der Schnittstelle zu überschreiben, müssen Sie alle Methoden der ursprünglichen Schnittstelle
einbeziehen.

Sie können die Werte, die Sie überschreiben möchten, zuerst weglassen und dann neu definieren. Können wir @ZSkycats Antwort zur Lösung machen?
Zeachco

Downvote für die Bezeichnung von Java als "andere Sprachen"
wvdz

@wvdz nicht, dass mir die Ablehnung sehr am Herzen liegt, aber wovon redest du? Wo hat sich jemand überhaupt auf Java bezogen? Das Durchsuchen der Seite nach "Java" hat nur einen Fund und ist in Ihrem Kommentar enthalten.
Nitzan Tomer

Vielleicht war ich ein bisschen mürrisch, aber es hat mich nur ein bisschen geärgert, dass Sie "andere Sprachen" gesagt haben, als Sie es gerade hätten sagen können, wie in Java. Oder gibt es wirklich viele andere Sprachen, die Object als universelle Basisklasse haben? Ich kenne C #, aber natürlich wurde C # stark von Java inspiriert.
wvdz

186

Ich benutze eine Methode, die zuerst die Felder filtert und sie dann kombiniert.

Referenz Eigenschaft vom Typ ausschließen

interface A {
    x: string
}

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

für Schnittstelle:

interface A {
    x: string
}

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

2
Es ist toll, das zu wissen. Das Problem ist jedoch, dass das vorhandene immer noch nicht geändert wird.
Freewind

6
Genau das habe ich gesucht. So habe ich erwartet, dass das Typoskript extendstandardmäßig funktioniert, aber leider Omitbehebt dieses kleine Problem alles Daw
Dawson B

1
Die Erweiterung der Benutzeroberfläche war genau das, wonach ich gesucht habe, danke!
mhodges

1
Hinweis: Sie benötigen das oben beschriebene Typoskript 3.5.3, um dies zu verwenden.
Vixson

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

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

Inspiriert von der extends Omit Lösung von ZSkycat habe ich mir Folgendes ausgedacht :

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

Beispiel:

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

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

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

Schritt für Schritt gehen:

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


8
Dies ist eine großartige Lösung.
Austin Brunkhorst

1
Noob hier, aber Sie wechseln von einer Schnittstelle zu einem Typ in Ihrem Beispiel nein? Oder gibt es keinen Unterschied?
Dominic

Niceeeeeeeeee: D
SaMiCoOo

1
@ Dominic Guter Punkt, ich habe die Antwort aktualisiert. Zwei gleichnamige Schnittstellen können zusammengeführt werden. typescriptlang.org/docs/handbook/…
Qwerty

32

Wenn Sie die Antwort von @ zSkycat ein wenig erweitern, können Sie ein Generikum erstellen, das zwei Objekttypen akzeptiert und einen zusammengeführten Typ zurückgibt, wobei die Mitglieder des zweiten die Mitglieder des ersten überschreiben.

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
Sehr cool :-) Ich habe dies bereits mit ein oder zwei Eigenschaften mit Omit gemacht, aber das ist viel cooler :-) Ich möchte oft einen Server-Entitätstyp 'erweitern' und einige Dinge ändern, die auf dem Client erforderlich oder optional sind .
Simon_Weaver

1
Dies sollte jetzt die akzeptierte Lösung sein. Der sauberste Weg, eine Schnittstelle zu "erweitern".
Manuhortet

5

Omit die Eigenschaft beim Erweitern der Schnittstelle:

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

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

3

Es ist lustig, dass ich den Tag damit verbringe, die Möglichkeit zu untersuchen, den gleichen Fall zu lösen. Ich habe festgestellt, dass dies nicht möglich ist:

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

Ursache Ein Modul kennt möglicherweise nicht alle verfügbaren Typen in Ihrer Anwendung. Und es ist ziemlich langweilig, alles von überall zu portieren und Code wie diesen zu machen.

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

Sie müssen den Typ später definieren, damit die automatische Vervollständigung gut funktioniert.


So können Sie ein bisschen schummeln:

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

Belassen Sie standardmäßig den Typ some, der die automatische Vervollständigung ermöglicht, wenn keine Überschreibungen erforderlich sind.

Dann

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

type SomeOtherType = {
  coolStuff: number
}

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

Deaktiviere die dumme Ausnahme hier mit der @ts-ignoreFlagge und sage uns, dass wir etwas falsch machen. Und lustig, dass alles wie erwartet funktioniert.

In meinem Fall reduziere ich die Scope-Vision des Typs x, sodass ich Code strenger ausführen kann. Zum Beispiel haben Sie eine Liste mit 100 Eigenschaften und reduzieren diese auf 10, um dumme Situationen zu vermeiden


1

Wenn jemand anderes einen generischen Dienstprogrammtyp benötigt, habe ich die folgende Lösung gefunden:

/**
 * 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 };

Ich brauchte das, weil in meinem Fall der Schlüssel zum Überschreiben ein Generikum selbst war.

Wenn Sie noch nicht Omitfertig sind, lesen Sie Ausschließen der Eigenschaft vom Typ .


1
Genau das habe ich gesucht, ich kann dir nicht genug danken: D: D: D
dwoodwardgb

@dwoodwardgb froh, dass es für jemand anderen nützlich war :-)
Toni

1

Um die Art der Immobilie einzugrenzen, extendfunktioniert einfach perfekt, wie in Nitzans Antwort :

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Um den Typ zu erweitern oder allgemein zu überschreiben , können Sie die Lösung von Zskycat ausführen :

interface A {
    x: string
}

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

Wenn Ihre Schnittstelle Ajedoch eine allgemeine Schnittstelle erweitert, verlieren Sie Abei Verwendung die verbleibenden Eigenschaften der benutzerdefinierten Typen Omit.

z.B

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! 

Der Grund ist, dass Omitintern nur Exclude<keyof A, 'x'>Schlüssel besprochen werden, die string | numberin unserem Fall der allgemeine sind . Also, Bwürde {x: number; }und akzeptiert jede zusätzliche Eigenschaft mit der Art von number | string | boolean.


Um dies zu beheben, habe ich mir einen anderen OverridePropsDienstprogrammtyp ausgedacht:

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

Beispiel:

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!
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.