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

Relaterade artiklar