Typescript d.ts dosyasında tanımlanan arayüz özellik türünü geçersiz kılma


132

Bir *.d.tstypcript'te tanımlanan arayüz özelliği türünü değiştirmenin bir yolu var mı ?

örneğin: içindeki bir arayüz x.d.tsşu şekilde tanımlanır:

interface A {
  property: number;
}

Yazdığım daktilo dosyalarında değiştirmek istiyorum

interface A {
  property: Object;
}

ya da bu bile işe yarar

interface B extends A {
  property: Object;
}

Bu yaklaşım işe yarayacak mı? Sistemimi denediğimde işe yaramadı. Mümkün olup olmadığını teyit etmek ister misin?

Yanıtlar:


58

Mevcut bir mülkün türünü değiştiremezsiniz.

Bir mülk ekleyebilirsiniz:

interface A {
    newProperty: any;
}

Ancak bir tür mevcut olanı değiştirmek:

interface A {
    property: any;
}

Bir hatayla sonuçlanır:

Sonraki değişken bildirimleri aynı türe sahip olmalıdır. "Özellik" değişkeni "sayı" türünde olmalıdır, ancak burada "herhangi biri" türü vardır

Elbette mevcut olanı genişleten kendi arayüzünüze sahip olabilirsiniz. Bu durumda, bir türü yalnızca uyumlu bir türle geçersiz kılabilirsiniz, örneğin:

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Bu arada, muhtemelen Objecttür olarak kullanmaktan kaçınmalısınız , bunun yerine türü kullanın any.

In için docs anytip belirtir:

Herhangi bir tür, mevcut JavaScript ile çalışmanın güçlü bir yoludur ve derleme sırasında tür denetimini kademeli olarak etkinleştirmenize ve devre dışı bırakmanıza olanak tanır. Object'in diğer dillerde olduğu gibi benzer bir rol oynamasını bekleyebilirsiniz. Ancak Object türündeki değişkenler yalnızca onlara herhangi bir değer atamanıza izin verir - bunlara keyfi yöntemler çağıramazsınız, gerçekte var olanları bile :

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 ile, arabirimi genişleterek yöntem türlerinin üzerine yazmak için orijinal arabirimdeki tüm yöntemleri eklemeniz gerekir, aksi takdirde türlerin uyumsuz olduğu hatası alırsınız bkz. Github.com/Microsoft/TypeScript/issues/978
jcubic

Önce üzerine yazmak istediğiniz değerleri çıkarabilir, sonra onları yeniden tanımlayabilirsiniz, @ZSkycat'in yanıtını çözen olanı yapabilir miyiz?
zeachco

Java'dan 'diğer diller' olarak bahsetmek için olumsuz oy verin
wvdz

@wvdz olumsuz oyu çok önemsediğimden değil, ama sen neden bahsediyorsun? nerede kimse java'dan bahsetti? Sayfada "java" araması yalnızca bir tane bulur ve yorumunuzda bulunur.
Nitzan Tomer

Belki biraz huysuzdum ama Java'da olduğu gibi, söyleyebildiğin zaman "diğer diller" demen beni biraz sinirlendirdi. Yoksa gerçekten Object'in evrensel temel sınıf olduğu birçok başka dil var mı? C # biliyorum ama elbette C # Java'dan büyük ölçüde esinlenmiştir.
wvdz

236

Önce alanları filtreleyen ve sonra birleştiren bir yöntem kullanıyorum.

başvuru Türü türden hariç tut

interface A {
    x: string
}

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

arayüz için:

interface A {
    x: string
}

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

3
Bunu bilmek harika. Ancak sorun şu ki, mevcut olanı hala değiştirmiyor.
Freewind

10
Bu tam olarak aradığım şeydi. Daktilo yazısının extendvarsayılan olarak bu şekilde çalışmasını bekliyordum , ama ne yazık ki, bu küçük Omither şeyi düzeltir 🙌
Dawson B

1
Arayüzü genişletmek tam olarak aradığım şeydi, teşekkürler!
mhodges

1
Not: Bunu kullanmak için yukarıdaki 3.5.3 yazı tipine ihtiyacınız olacak.
Vixson

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

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

ZSkycat'ın extends Omit çözümünden esinlenerek şunu buldum :

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

Misal:

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

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

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

Adım adım ilerlemek:

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 Yardımcı Program Türleri


9
Bu harika bir çözüm.
Austin Brunkhorst

1
Noob burada ama bir arayüzden örneğinizdeki bir türe geçiş yapıyorsunuz hayır? Yoksa fark yok mu?
Dominic

Niceeeeeeeeee: D
SaMiCoOo

1
@Dominic İyi nokta, cevabı güncelledim. Aynı ada sahip iki Arayüz birleştirilebilir. typescriptlang.org/docs/handbook/…
Qwerty

35

@ ZSkycat'in cevabını biraz genişleterek, iki nesne türünü kabul eden ve ikincinin üyeleriyle birincinin üyelerini geçersiz kılarak birleştirilmiş bir tür döndüren bir jenerik yaratabilirsiniz.

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
Çok güzel :-) Bunu daha önce Atlanmış bir veya iki özellik ile yaptım, ancak bu çok daha havalı :-) Genellikle bir sunucu varlık türünü 'genişletmek' ve istemcide gerekli veya isteğe bağlı bazı şeyleri değiştirmek istiyorum .
Simon_Weaver

1
Şimdi kabul edilen çözüm bu olmalı. Bir arabirimi "genişletmenin" en temiz yolu.
manuhortet

12

Omit arabirimi genişletirken özellik:

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

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

3

Günü aynı davayı çözme olasılığını araştırmakla geçirmem çok komik. Bu şekilde yapmanın mümkün olmadığını buldum:

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

Neden Bir modül, uygulamanızdaki mevcut tüm türleri bilmiyor olabilir. Ve her şeyi her yerden taşımak oldukça sıkıcı ve bunun gibi kodlar yapmak.

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

Otomatik tamamlamanın iyi çalışması için türü daha sonra tanımlamanız gerekir.


Yani biraz hile yapabilirsiniz:

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

Varsayılan olarak, geçersiz kılmalar gerekmediğinde otomatik tamamlamanın çalışmasına izin veren bir tür bırakıldı.

Sonra

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

type SomeOtherType = {
  coolStuff: number
}

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

Burada @ts-ignorebayrak kullanarak aptal istisnayı devre dışı bırakın , bize yanlış bir şey yaptığımızı söyleyin. Ve komik olan her şey beklendiği gibi çalışıyor.

Benim durumumda, türün kapsam vizyonunu azaltıyorum x, bu kod daha sıkı yapmama izin veriyor. Örneğin, 100 mülk listeniz var ve aptal durumlardan kaçınmak için bunu 10'a düşürdünüz.


3

Nitzan'ın cevabındaextend olduğu gibi , mülkün türünü daraltmak için, basit mükemmel çalışır :

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

Türü genişletmek veya genel olarak geçersiz kılmak için Zskycat'in çözümünü uygulayabilirsiniz :

interface A {
    x: string
}

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

Ancak, arabiriminiz Agenel bir arabirimi genişletiyorsa, Akullanırken kalan özelliklerin özel türlerini kaybedersiniz Omit.

Örneğin

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! 

Nedeni, Omitdahili olarak yalnızca bizim durumumuzda Exclude<keyof A, 'x'>genel olacak anahtarların üzerinden geçer string | number. Yani, türüyle herhangi bir ekstra özellik Bolur {x: number; }ve kabul eder number | string | boolean.


Bunu düzeltmek için, OverridePropsaşağıdaki gibi farklı bir yardımcı program türü buldum :

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

Misal:

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

Başka birinin bunu yapmak için genel bir yardımcı program türüne ihtiyacı varsa, aşağıdaki çözümü buldum:

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

Buna ihtiyacım vardı çünkü benim durumumda, geçersiz kılma anahtarı bir jenerikti.

OmitHazır değilseniz , özelliği türden hariç tutma konusuna bakın .


1
Tam olarak aradığım şey buydu, size yeterince teşekkür edemem: D: D: D
dwoodwardgb

@dwoodwardgb başkası için yararlı olduğuna sevindim :-)
Toni

0

NOT: Bu cevapta kullandığım sözdiziminin eski cevaplar yazılırken mevcut olup olmadığından emin değilim, ancak bu soruda bahsedilen örneğin nasıl çözüleceğine dair daha iyi bir yaklaşım olduğunu düşünüyorum.


Bu konuyla ilgili bazı sorunlar yaşadım (arayüz özelliklerinin üzerine yazma) ve bunu şu şekilde ele alıyorum:

  1. Öncelikle, kullanmak istediğiniz olası türlerle genel bir arayüz oluşturun.

defaultGenel parametre için de görebileceğiniz gibi bir değer seçebilirsiniz .<T extends number | SOME_OBJECT = number>

type SOME_OBJECT = { foo: "bar" }

interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
  property: T;
}
  1. Ardından, genel parametreye bir değer ileterek (veya atlayıp varsayılanı kullanarak) bu sözleşmeye dayalı olarak yeni türler oluşturabilirsiniz:
type A_NUMBER = INTERFACE_A;                   // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT>  // MAKES { property: SOME_OBJECT }

Ve sonuç bu:

const aNumber: A_NUMBER = {
    property: 111  // THIS EXPECTS A NUMBER
}

const anObject: A_SOME_OBJECT = {
    property: {   // THIS EXPECTS SOME_OBJECT
        foo: "bar"
    }
}

Typescript oyun alanı


0

Benim gibi tembel insanlar için kısa cevap:

type Overrided = Omit<YourInterface, 'overrideField'> & { overrideField: <type> }; 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.