typescript - Villkorlig typ för tomma objekt
Är det möjligt att ha en villkorlig typ som kan testa för objekt som kan vara tomma?
Till exempel:
function test<T>(a: T): T extends {} ? string : never {
return null
}
let o1: {}
let o2: { fox? }
let o3: { fox }
test(o1)
test(o2)
test(o3) // this one should be 'never'
Eftersom det villkorliga typtestet också gäller för arv, ger alla 3 fallen 'string', men jag vill ge 'never' om åtminstone en egenskap hos typen är obligatorisk (icke-tom objekttyp som o3)
UPPDATERING
När jag skrev denna fråga försökte jag fastställa orsaken till ett problem jag hade. Jag trodde att jag borde lösa min tvekan och inte mitt problem och också förenkla min fråga. Men svaren avviker från mitt problem.
I princip försökte jag bygga en funktion där det första argumentet är ett objekt och det andra är valfritt om det första kan vara helt partiellt (initierat med {})
function test<T extends {}>(a: T, ...x: T extends {} ? [never?] : [any])
function test(a, b) {
return null
}
let o1: {}
let o2: { fox? }
let o3: { fox }
test(o1) // ok
test(o2) // ok
test(o3) // should fail and require second argument
Svar #1
Du kan göra det på ett relativt enkelt sätt med keyof-operatorn.
Kom ihåg att never i huvudsak är motsvarigheten till den tomma mängden
type Foo = keyof {} // never
type Bar = keyof { a: 1, b: 2 } // "a" | "b"
Tillämpat på ditt användningsfall blir detta
declare function f<T>(t: T): keyof T extends never ? string : never
const a = f({}) //string
const b = f({ a: 1 }) //never
redigera
Jag är inte säker på att jag helt förstår det användningsfall du nu har inkluderat i frågan, men om idén är att behandla ett objekt med endast valfria som ett tomt objekt, kan du göra det med lite mer typmaskineri.
Låt oss låna definitionen av OptionalPropertyOf från den här frågan.
Givet ett objekt av typen T där T utökar object, kan vi definiera följande
type OptionalPropertyOf<T extends object> = Exclude<{
[K in keyof T]: T extends Record<K, T[K]>
? never
: K
}[keyof T], undefined>
type ExcludeOptionalPropertyOf<T extends object> = Omit<T, OptionalPropertyOf<T>>
type Foo = { a: number, b: string }
type OptFoo = OptionalPropertyOf<Foo> // never
type NonOptFoo = ExcludeOptionalPropertyOf<Foo> // { a: number, b: string }
type Bar = { a: number, b?: string }
type OptBar = OptionalPropertyOf<Bar> // "b"
type NonOptBar = ExcludeOptionalPropertyOf<Bar> // { a: number }
type Baz = { a?: number, b?: string }
type OptBaz = OptionalPropertyOf<Baz> // "a" | "b"
type NonOptBaz = ExcludeOptionalPropertyOf<Baz> // {}
Låt oss då ändra definitionen av f lite grann till
declare function f<T extends object>(t: T): keyof ExcludeOptionalPropertyOf<T> extends never ? string : never
Och nu får du det du letade efter
declare const a: Foo
declare const b: Bar
declare const c: Baz
declare const d: {}
const fa = f(a) // never
const fb = f(b) // never
const fc = f(c) // string
const fd = f(d) // string
Lekplatslänk
Svar #2
Detta använder inte en villkorlig typ, och det använder inte riktigt samma kod som din fråga; så jag förstår att det kan vara helt värdelöst. Men jag tänkte att jag skulle lägga in något för att se om det fungerar som en utgångspunkt för att arbeta mot ett användbart svar.
Du kan använda överlagringar för att uppnå samma sak:
function test(a: {}): string;
function test(a: Record<string | number | symbol, any>): never;
function test(a: {} | Record<string | number | symbol, any>): string | never {
if (Object.keys(a).length === 0) {
return 'test';
} else {
throw new Error();
}
}
const a = test({}); // type string
const b = test({ a: 1 }); // type never
Lekplatslänk
