Urobiť jednu vlastnosť voliteľnou v TypeScripte
V TypeScripte 2.2...
Povedzme, že mám typ Osoba:
interface Person {
name: string;
hometown: string;
nickname: string;
}
A rád by som vytvoril funkciu, ktorá vráti osobu, ale nevyžaduje prezývku:
function makePerson(input: ???): Person {
return {...input, nickname: input.nickname || input.name};
}
Aký by mal byť typ input? Hľadám dynamický spôsob, ako určiť typ, ktorý je identický s Person, okrem toho, že nickname je voliteľný (nickname?: string | undefined). Najbližšie, čo som doteraz zistil, je toto:
type MakePersonInput = Partial<Person> & {
name: string;
hometown: string;
}
ale to nie je celkom to, čo hľadám, pretože musím špecifikovať všetky typy, ktoré sú povinné, namiesto tých, ktoré sú voliteľné.
Odpoveď č. 1
Môžete urobiť aj niečo takéto, čiastočne len niektoré kľúče.
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = PartialBy<Person, 'nickname'>
Odpoveď č. 2
Tu je môj voliteľný typ utility pre Typescript 3.5+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
// and your use case
type MakePersonInput = Optional<Person, 'nickname'>
// and if you wanted to make the hometown optional as well
type MakePersonInput = Optional<Person, 'hometown' | 'nickname'>
Odpoveď č. 3
Pre riešenie typu plug and play zvážte použitie vynikajúceho balíka utility-types:
npm i utility-types --save
Potom jednoducho použite Optional<T, K>:
import { Optional } from 'utility-types';
type Person = {
name: string;
hometown: string;
nickname: string;
}
type PersonWithOptionalNickname = Optional<Person, 'nickname'>;
// Expect:
//
// type PersonWithOptionalNickname {
// name: string;
// hometown: string;
// nickname?: string;
// }
Odpoveď č. 4
Aktualizácia:
Od verzie TypeScript 2.8 je to podporované oveľa stručnejšie pomocou podmienených typov! Doteraz sa zdá, že to je tiež spoľahlivejšie ako predchádzajúce implementácie.
type Overwrite<T1, T2> = {
[P in Exclude<keyof T1, keyof T2>]: T1[P]
} & T2;
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = Overwrite<Person, {
nickname?: string;
}>
function makePerson(input: MakePersonInput): Person {
return {...input, nickname: input.nickname || input.name};
}
Ako predtým, MakePersonInput je ekvivalentné s:
type MakePersonInput = {
name: string;
hometown: string;
} & {
nickname?: string;
}
Zastaralé:
Od verzie TypeScript 2.4.1 to vyzerá, že je k dispozícii ďalšia možnosť, ako navrhol používateľ GitHub ahejlsberg v vlákne o odčítaní typov: https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;
interface Person {
name: string;
hometown: string;
nickname: string;
}
type MakePersonInput = Overwrite<Person, {
nickname?: string
}>
function makePerson(input: MakePersonInput): Person {
return {...input, nickname: input.nickname || input.name};
}
Podľa Intellisense je MakePersonInput ekvivalentné:
type MakePersonInput = {
name: string;
hometown: string;
} & {
nickname?: string;
}
ktorý vyzerá trochu smiešne, ale úplne splní svoju úlohu.
Na druhej strane, budem musieť chvíľu pozerať na ten typ Diff, kým začnem chápať, ako funguje.
Odpoveď č. 5
Balík type-fest má utilitu SetOptional - https://github.com/sindresorhus/type-fest/blob/main/source/set-optional.d.ts
import { SetOptional } from 'type-fest';
type PersonWithNicknameOptional = SetOptional<Person, 'nickname'>
Myslím si, že knižnica je dobre udržiavaná a podporuje najnovšie verzie TypeScriptu. Podľa môjho názoru stojí za to ju pridať do projektu v TypeScripte.
Odpoveď č. 6
Ak používate nedávnu verziu TypeScriptu, jednoduché riešenie je urobiť
function makePerson(input: Omit<Person, 'nickname'> & { nickname?: string }): Person {
return {...input, nickname: input.nickname || input.name};
}
V podstate odstránite vlastnosť „nickname“ z rozhrania a znova ju pridáte ako voliteľnú
Ak chcete mať istotu, že zostane synchronizované s pôvodným rozhraním, môžete urobiť
Omit<Person, 'nickname'> & Partial<Pick<Person, 'nickname'>>
ktorý vás upozorní, ak niekedy zmeníte vlastnosť „nickname“ v pôvodnom rozhraní
