type Require = {} extends { a: string } ? true : false // false
type OptionalProperty = {} extends { a?: string } ? true : false // true
type OptionalValue = {} extends { a: string | undefined } ? true : false
type Index = {} extends { [x: string]: any } ? true : false // true
https://stackoverflow.com/questions/51954558/how-can-i-remove-a-wider-type-from-a-union-type-without-removing-its-subtypes-in microsoft/TypeScript#12215 (comment)
type KnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K
} extends {[_ in keyof T]: infer U} ? U : never;
interface test {
req: string
opt: string
[k: string]: any
}
type Out = KnownKeys<test> // "req" | "opt"
interface test {
req: string
opt?: string
[k: string]: any
}
type RequiredKnownKeys<T> = {
[K in keyof T]: {} extends Pick<T, K> ? never : K
} extends { [_ in keyof T]: infer U } ? ({} extends U ? never : U) : never
type OptionalKnownKeys<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : {} extends Pick<T, K> ? K : never
} extends { [_ in keyof T]: infer U } ? ({} extends U ? never : U) : never
type c = RequiredKnownKeys<test> // ''req'
type d = OptionalKnownKeys<test> // 'opt'
{} расширяет U ? never : U - объект может быть пустым, эта проверка необходима что бы не возвращать пустой объектный тип
https://github.com/microsoft/TypeScript/pull/12528/files почему
при передачи типа string возвращает string