Remplacement du type de propriété d'interface défini dans le fichier Typescript d.ts


113

Existe-t-il un moyen de modifier le type de propriété d'interface défini dans un *.d.tsscript typographique?

par exemple: une interface dans x.d.tsest définie comme

interface A {
  property: number;
}

Je veux le changer dans les fichiers dactylographiés dans lesquels j'écris

interface A {
  property: Object;
}

ou même cela fonctionnerait

interface B extends A {
  property: Object;
}

Cette approche fonctionnera-t-elle? Cela n'a pas fonctionné lorsque j'ai essayé mon système. Vous voulez juste confirmer si c'est encore possible?

Réponses:


51

Vous ne pouvez pas modifier le type d'une propriété existante.

Vous pouvez ajouter une propriété:

interface A {
    newProperty: any;
}

Mais changer un type d'existant:

interface A {
    property: any;
}

Entraîne une erreur:

Les déclarations de variables suivantes doivent avoir le même type. La variable 'propriété' doit être de type 'nombre', mais ici a le type 'any'

Vous pouvez bien sûr avoir votre propre interface qui étend une interface existante. Dans ce cas, vous pouvez remplacer un type uniquement par un type compatible, par exemple:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Au fait, vous devriez probablement éviter d'utiliser Objectcomme type, utilisez plutôt le type any.

Dans la documentation pour le anytype, il indique:

Le type any est un moyen puissant de travailler avec JavaScript existant, vous permettant d'activer et de désactiver progressivement la vérification de type lors de la compilation. Vous pouvez vous attendre à ce que Object joue un rôle similaire, comme il le fait dans d'autres langues. Mais les variables de type Object vous permettent uniquement de leur attribuer une valeur quelconque - vous ne pouvez pas appeler de méthodes arbitraires sur elles, même celles qui existent réellement :

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

Avec Typescript> = 1.1 pour écraser le type des méthodes en étendant l'interface, vous devez inclure toutes les méthodes de l'interface d'origine, sinon vous obtiendrez une erreur indiquant que les types sont incompatibles voir github.com/Microsoft/TypeScript/issues/978
jcubic

vous pouvez Omettre les valeurs que vous voulez écraser en premier, puis les redéfinir, pouvons-nous faire de la réponse de @ZSkycat la solution?
zeachco le

Downvote pour faire référence à Java comme `` d'autres langues ''
wvdz

@wvdz pas que je me soucie beaucoup du vote négatif, mais de quoi parlez-vous? où est-ce que quelqu'un a même fait référence à java? la recherche de la page "java" n'a qu'une seule recherche et c'est dans votre commentaire.
Nitzan Tomer

Peut-être étais-je un peu grincheux, mais cela m'énervait un peu que vous disiez "autres langues" alors que vous auriez pu le dire, comme en Java. Ou y a-t-il vraiment beaucoup d'autres langages qui ont Object comme classe de base universelle? Je connais C #, mais bien sûr C # a été fortement inspiré par Java.
wvdz

201

J'utilise une méthode qui filtre d'abord les champs, puis les combine.

référence Exclure la propriété du type

interface A {
    x: string
}

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

pour l'interface:

interface A {
    x: string
}

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

2
C'est formidable de le savoir. Mais le problème est qu'il ne modifie toujours pas l'existant.
Freewind

8
C'était exactement ce que je cherchais. C'est ainsi que je m'attendais à ce que le dactylographie extendfonctionne par défaut, mais hélas, cela Omitcorrige tout
Dawson B

1
Étendre l'interface était exactement ce que je recherchais, merci!
mhodges

1
Remarque: vous aurez besoin de typescript 3.5.3 ci-dessus pour l'utiliser.
Vixson le

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

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

Inspiré par la extends Omit solution de ZSkycat , j'ai proposé ceci:

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

Exemple:

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

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

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

Aller étape par étape:

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

Types d'utilitaires TypeScript


8
C'est une excellente solution.
Austin Brunkhorst

1
Noob ici mais vous passez d'une interface à un type dans votre exemple non? Ou n'y a-t-il aucune différence?
Dominic le

Niceeeeeeeeee: D
SaMiCoOo

1
@Dominic Bon point, j'ai mis à jour la réponse. Deux interfaces portant le même nom peuvent fusionner. typescriptlang.org/docs/handbook/…
Qwerty

33

En étendant un peu la réponse de @ zSkycat, vous pouvez créer un générique qui accepte deux types d'objet et renvoie un type fusionné avec les membres du second remplaçant les membres du premier.

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
Très cool :-) J'ai déjà fait cela avec une ou deux propriétés avec Omit, mais c'est beaucoup plus cool :-) Je veux souvent `` étendre '' un type d'entité serveur et changer certaines choses pour être obligatoires ou facultatives sur le client .
Simon_Weaver

1
Cela devrait être la solution acceptée maintenant. Le moyen le plus propre "d'étendre" une interface.
manuhortet

7

Omit la propriété lors de l'extension de l'interface:

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

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

3

C'est drôle que je passe la journée à étudier la possibilité de résoudre le même cas. J'ai trouvé qu'il n'était pas possible de faire de cette façon:

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

Cause Un module peut ne pas connaître tous les types disponibles dans votre application. Et c'est assez ennuyeux de tout porter de partout et de faire du code comme celui-ci.

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

Vous devez définir le type plus tard pour que la saisie semi-automatique fonctionne correctement.


Vous pouvez donc tricher un peu:

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

A laissé le type par défaut, qui autorise les travaux de saisie semi-automatique, lorsque les remplacements ne sont pas nécessaires.

ensuite

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

type SomeOtherType = {
  coolStuff: number
}

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

Désactivez l'exception stupide ici en utilisant le @ts-ignoredrapeau, en nous disant que nous faisons quelque chose de mal. Et chose drôle, tout fonctionne comme prévu.

Dans mon cas, je réduis la vision de la portée du type x, cela me permet de faire du code plus strict. Par exemple vous avez une liste de 100 propriétés, et vous la réduisez à 10, pour éviter les situations stupides


2

Pour restreindre le type de propriété, les extendtravaux simples sont parfaits, comme dans la réponse de Nitzan :

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Pour l' élargissement, ou en général remplaçant le type, vous pouvez faire la solution de Zskycat :

interface A {
    x: string
}

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

Mais, si votre interface Aétend une interface générale, vous perdrez les types personnalisés de Apropriétés restantes lors de l'utilisation Omit.

par exemple

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! 

La raison est que, en Omitinterne, ne passe que par les Exclude<keyof A, 'x'>clés qui seront le général string | numberdans notre cas. Ainsi, Bdeviendrait {x: number; }et accepte toute propriété supplémentaire avec le type de number | string | boolean.


Pour résoudre ce problème, j'ai proposé un OverridePropstype d'utilitaire différent comme suit:

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

Exemple:

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

Si quelqu'un d'autre a besoin d'un type d'utilitaire générique pour ce faire, j'ai proposé la solution suivante:

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

J'en avais besoin car dans mon cas, la clé à remplacer était un générique lui-même.

Si vous n'êtes pas Omitprêt, consultez Exclure la propriété du type .


1
C'est exactement ce que je cherchais, je ne
saurais

@dwoodwardgb heureux que cela ait été utile pour quelqu'un d'autre :-)
Toni
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.