Nie możesz zmienić typu istniejącej usługi.
Możesz dodać usługę:
interface A {
newProperty: any;
}
Ale zmiana typu istniejącego:
interface A {
property: any;
}
Powoduje błąd:
Kolejne deklaracje zmiennych muszą mieć ten sam typ. Zmienna „właściwość” musi być typu „liczba”, ale tutaj ma typ „dowolna”
Możesz oczywiście mieć własny interfejs, który rozszerzy istniejący. W takim przypadku możesz zastąpić typ tylko zgodnym typem, na przykład:
interface A {
x: string | number;
}
interface B extends A {
x: number;
}
Nawiasem mówiąc, prawdopodobnie powinieneś unikać używania Object
jako typu, zamiast tego użyj typu any
.
W dokumentach tego any
typu stwierdza się:
Dowolny typ to potężny sposób pracy z istniejącym JavaScriptem, umożliwiający stopniowe włączanie i wyłączanie sprawdzania typów podczas kompilacji. Można się spodziewać, że Object będzie odgrywał podobną rolę, jak w innych językach. Ale zmienne typu Object pozwalają tylko na przypisanie im dowolnej wartości - nie można wywołać na nich dowolnych metod, nawet tych, które faktycznie istnieją :
let notSure: any = 4;
notSure.ifItExists();
notSure.toFixed();
let prettySure: Object = 4;
prettySure.toFixed();
Używam metody, która najpierw filtruje pola, a następnie je łączy.
odwołanie Wyklucz właściwość z typu
interface A { x: string } export type B = Omit<A, 'x'> & { x: number };
dla interfejsu:
interface A { x: string } interface B extends Omit<A, 'x'> { x: number }
źródło
extend
będzie działał domyślnie, ale niestety ten małyOmit
naprawia wszystko 🙌Zainspirowany rozwiązaniem ZSkycat
extends Omit
wymyśliłem to:Przykład:
interface OriginalInterface { a: string; b: boolean; c: number; } type ModifiedType = Modify<OriginalInterface , { a: number; b: number; }> // ModifiedType = { a: number; b: number; c: number; }
Idąc krok po kroku:
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; }
Typy narzędzi TypeScript
źródło
Rozszerzając nieco odpowiedź @ zSkycat, możesz utworzyć rodzaj generyczny, który akceptuje dwa typy obiektów i zwraca scalony typ, w którym składowe drugiego zastępują elementy pierwszego.
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;
źródło
Omit
właściwość przy rozszerzaniu interfejsu:interface A { a: number; b: number; } interface B extends Omit<A, 'a'> { a: boolean; }
źródło
To zabawne, że spędzam cały dzień na badaniu możliwości rozwiązania tej samej sprawy. Okazało się, że nie można tego zrobić:
// 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; }
Przyczyna Moduł może nie wiedzieć o wszystkich typach dostępnych w twojej aplikacji. I jest to dość nudne przenoszenie wszystkiego z dowolnego miejsca i robienie kodu w ten sposób.
export interface A { x: A | B | C | D ... Million Types Later }
Musisz później zdefiniować typ, aby autouzupełnianie działało dobrze.
Możesz więc trochę oszukać:
// a.ts - module export interface A { x: string; }
Pozostawiono domyślnie jakiś typ, który zezwala na autouzupełnianie, gdy nadpisania nie są wymagane.
Następnie
// b.ts - module import {A} from './a'; type SomeOtherType = { coolStuff: number } // @ts-ignore interface B extends A { x: SomeOtherType; }
Wyłącz głupi wyjątek, używając
@ts-ignore
flagi, mówiąc nam, że robimy coś źle. I zabawne, wszystko działa zgodnie z oczekiwaniami.W moim przypadku ograniczam zakres wizji typu
x
, pozwala mi to robić kod w bardziej restrykcyjny sposób. Na przykład masz listę 100 nieruchomości i zmniejszasz ją do 10, aby uniknąć głupich sytuacjiźródło
Aby zawęzić rodzaj nieruchomości, proste
extend
działa idealnie, jak w odpowiedzi Nitzana :interface A { x: string | number; } interface B extends A { x: number; }
Dla poszerzenia lub ogólnie nadrzędnymi typu można zrobić rozwiązanie Zskycat za :
interface A { x: string } export type B = Omit<A, 'x'> & { x: number };
Jeśli jednak twój interfejs
A
rozszerza ogólny interfejs,A
podczas używania utracisz niestandardowe typy pozostałych właściwościOmit
.na przykład
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!
Powodem jest to, że
Omit
wewnętrznie przechodzą tylko przezExclude<keyof A, 'x'>
klucze, które będą generalnestring | number
w naszym przypadku. Tak więcB
stałoby się{x: number; }
i akceptuje każdą dodatkową właściwość typunumber | string | boolean
.Aby to naprawić, wymyśliłem inny
OverrideProps
typ narzędzia:type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };
Przykład:
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!
źródło
Jeśli ktoś potrzebuje do tego ogólnego typu narzędzia, wymyśliłem następujące rozwiązanie:
/** * 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 };
Potrzebowałem tego, ponieważ w moim przypadku klucz do zastąpienia był sam w sobie generyczny.
Jeśli nie masz jeszcze
Omit
gotowego, zobacz Wykluczanie właściwości z typu .źródło
UWAGA: Nie jestem pewien, czy składnia, której używam w tej odpowiedzi, była dostępna, gdy pisano starsze odpowiedzi, ale myślę, że jest to lepsze podejście do rozwiązania przykładu wspomnianego w tym pytaniu.
Miałem kilka problemów związanych z tym tematem (nadpisywanie właściwości interfejsu) i tak sobie z tym radzę:
Możesz nawet użyć opcji wybierz
default
wartość dla parametru ogólnego, jak widać w<T extends number | SOME_OBJECT = number>
type SOME_OBJECT = { foo: "bar" } interface INTERFACE_A <T extends number | SOME_OBJECT = number> { property: T; }
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 }
A oto wynik:
const aNumber: A_NUMBER = { property: 111 // THIS EXPECTS A NUMBER } const anObject: A_SOME_OBJECT = { property: { // THIS EXPECTS SOME_OBJECT foo: "bar" } }
Maszynopisowy plac zabaw
źródło