typescript - Kontrollera om ett gränssnitt har ett obligatoriskt fält
Är det möjligt att kontrollera om ett gränssnitt har ett obligatoriskt fält med hjälp av Typescripts villkorliga typer?
type AllRequired = { a: string; b: string }
type PartiallyRequired = { a: string; b?: string }
type Optional = { a?: string; b?: string }
// Is it possible to change this, so the below works
type HasRequiredField<T> = T extends {} ? true : false
type A = HasRequiredField<AllRequired> // true
type B = HasRequiredField<PartiallyRequired> // true
type C = HasRequiredField<Optional> // false
Svar #1
Ja, du kan upptäcka om egenskaper är valfria. Det blir lite osäkert med indexsignaturer, men dina typer har inte dem så jag kommer inte att oroa mig för dem.
Här är hur du kan extrahera bara nycklarna för de icke-valfria egenskaperna av en objekttyp
type RequiredKeys<T> = { [K in keyof T]-?:
({} extends { [P in K]: T[K] } ? never : K)
}[keyof T]
Och sedan kan du bara kontrollera om det har någon av dem eller inte (om RequiredKeys<T> är never så har det inte):
type HasRequiredField<T> = RequiredKeys<T> extends never ? false : true
Och det ger dina önskade resultat:
type A = HasRequiredField<AllRequired> // true
type B = HasRequiredField<PartiallyRequired> // true
type C = HasRequiredField<Optional> // false
Hoppas det hjälper; lycka till!
Svar #2
Hittade ett annat, enklare sätt att göra detta som fungerar åtminstone i TS 3.8.3.
// if Partial<T> extends T none of the fields are required.
type HasRequiredFields<T> = Partial<T> extends T ? false : true
Exempel på användningsfall
type Info<TInfo> = Partial<TInfo> extends TInfo
? { info?: TInfo }
: { info: TInfo };
type A = { type: 'A' } & Info<{ abc?: string }>
type B = { type: 'B' } & Info<{ abc: string }>
// Valid
let a1: A = { type: 'A' };
// Valid
let a2: A = { type: 'A', info: {} };
// Valid
let a3: A = { type: 'A', info: { abc: 'hello' } };
// Invalid
let a4: A = { type: 'A', info: { abc: 3 } };
// Invalid
let b1: B = { type: 'B' }
// Invalid
let b2: B = { type: 'B', info: {} };
// Valid
let b3: B = { type: 'B', info: { abc: 'Hello' } };
// Invalid
let b4: B = { type: 'B', info: { abc: 5 } };
