Last active
March 29, 2026 13:01
-
-
Save 5cover/da8d98fbdc162b8a15696fda80e75d3e to your computer and use it in GitHub Desktop.
Typing `typeof`: Typeof<T>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import type { Typeof } from './typeof'; | |
| type TypeofString = TypeofStringDefined | 'undefined'; | |
| type TypeofStringDefined = 'object' | 'function' | 'string' | 'number' | 'bigint' | 'boolean' | 'symbol'; | |
| type Recursive = { next: Recursive }; | |
| type RecursiveTest = Test<Same<Typeof<Recursive>, 'object'>>; | |
| type Conditional<T> = T extends string ? number : boolean; | |
| type TestConditional = Test<Same<Typeof<Conditional<string | number>>, 'number' | 'boolean'>>; | |
| declare const u: unique symbol; | |
| type TestUniqueSymbol = Test<Same<Typeof<typeof u>, 'symbol'>>; | |
| enum Enum { | |
| A, | |
| } | |
| type TestEnum = Test<Same<Typeof<Enum>, 'number'>> | Test<Same<Typeof<typeof Enum>, 'object'>>; | |
| type EmptyMapped<T> = { [K in keyof T]: never }; | |
| type TestEmptyMapped = Test<Same<Typeof<EmptyMapped<{}>>, TypeofStringDefined>>; | |
| type Overload = { | |
| (x: string): number; | |
| (x: number): string; | |
| }; | |
| type TestOverload = Test<Same<Typeof<Overload>, 'function'>>; | |
| type Tests = | |
| | Test<Same<Typeof<{ [k: string]: any }>, 'object'|'function'>> | |
| | Test<Same<Typeof<string & never>, never>> | |
| | Test<Same<Typeof<never | string>, 'string'>> | |
| | Test<Same<Typeof<string & { length: number }>, 'string' | 'object'>> | |
| | Test<Same<Typeof<string & number>, never>> | |
| | Test<Same<Typeof<string & { __brand?: 'x' }>, 'string' | 'object'>> | |
| | Test<Same<Typeof<any & string>, TypeofString>> | |
| | Test<Same<Typeof<unknown & string>, 'string'>> | |
| | Test<Same<Typeof<[1, 2, 3]>, 'object'>> | |
| | Test<Same<Typeof<readonly [1, 2]>, 'object'>> | |
| | Test<Same<Typeof<`a${number}`>, 'string'>> | |
| | Test<Same<Typeof<Uppercase<'abc'>>, 'string'>> | |
| | Test<Same<Typeof<'' | 0>, 'string' | 'number'>> | |
| | Test<Same<Typeof<'a' | 'b'>, 'string'>> | |
| | Test<Same<Typeof<(() => void) | null>, 'object' | 'function'>> | |
| | Test<Same<Typeof<{ (): number }>, 'function'>> | |
| | Test<Same<Typeof<((x: number) => string) & { foo: 1 }>, 'function'>> | |
| | Test<Same<Typeof<((x: number) => string) & Record<string, never>>, 'function'>> | |
| | Test<Same<Typeof<((x: number) => void) & { [k: string]: never }>, 'function'>> | |
| | Test<Same<Typeof<[]>, 'object'>> | |
| | Test<Same<Typeof<[1, 2, 3]>, 'object'>> | |
| | Test<Same<Typeof<{ [k in 'a' | 'b']: number }>, 'object'>> | |
| | Test<Same<Typeof<{ [k in 1 | 2]: number }>, 'object'>> | |
| | Test<Same<Typeof<{ [K in never]: never }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{ [k in symbol]: number }>, 'object'>> | |
| | Test<Same<Typeof<{ [k: number]: string }>, 'object'|'string'>> | |
| | Test<Same<Typeof<{ [k: number]: string; length: number }>, 'object'|'string'>> | |
| | Test<Same<Typeof<{ [k: string]: never } | (() => void)>, 'object' | 'function'>> | |
| | Test<Same<Typeof<{ [k: string]: string }>, 'object'>> | |
| | Test<Same<Typeof<{ [k: string]: string | number; fixed: 1 }>, 'object'>> | |
| | Test<Same<Typeof<{ [k: symbol]: number }>, 'object'>> | |
| | Test<Same<Typeof<{ a: 1 } & { b: 2 }>, 'object'>> | |
| | Test<Same<Typeof<{ a: 1 } | (() => void)>, 'object' | 'function'>> | |
| | Test<Same<Typeof<{ a: 1 } | { b: 2 }>, 'object'>> | |
| | Test<Same<Typeof<{ a: 1 } | null>, 'object'>> | |
| | Test<Same<Typeof<{ a: 1 } | undefined>, 'object' | 'undefined'>> | |
| | Test<Same<Typeof<{ a: 1; b?: 2 }>, 'object'>> | |
| | Test<Same<Typeof<{ a?: 1 }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{ foo: string } & ((x: number) => string)>, 'function'>> | |
| | Test<Same<Typeof<{ items: string[] }>, 'object'>> | |
| | Test<Same<Typeof<{ readonly a: 1 }>, 'object'>> | |
| | Test<Same<Typeof<{ readonly id: string; name: string }>, 'object'>> | |
| | Test<Same<Typeof<{ type: 'ok' }>, 'object'>> | |
| | Test<Same<Typeof<{ type: 'ok'; value: 123 }>, 'object'>> | |
| | Test<Same<Typeof<{ user: { id: string } }>, 'object'>> | |
| | Test<Same<Typeof<{} & { a: 1 }>, 'object'>> | |
| | Test<Same<Typeof<{} & string>, 'string'>> | |
| | Test<Same<Typeof<{} | { a: 1 }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{} | null>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{} | Record<string, never>>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{} | undefined>, TypeofStringDefined | 'undefined'>> | |
| | Test<Same<Typeof<{}>, TypeofStringDefined>> | |
| | Test<Same<Typeof<1 | 2 | 3>, 'number'>> | |
| | Test<Same<Typeof<1n | 2n>, 'bigint'>> | |
| | Test<Same<Typeof<abstract new (...args: any[]) => {}>, 'function'>> | |
| | Test<Same<Typeof<any>, TypeofString>> | |
| | Test<Same<Typeof<Array<{ id: string }>>, 'object'>> | |
| | Test<Same<Typeof<bigint>, 'bigint'>> | |
| | Test<Same<Typeof<BigInt>, 'bigint'|'object'>> | |
| | Test<Same<Typeof<boolean>, 'boolean'>> | |
| | Test<Same<Typeof<Boolean>, 'boolean' | 'object'>> | |
| | Test<Same<Typeof<ConstructorParameters<new (x: number) => Date>>, 'object'>> | |
| | Test<Same<Typeof<Date>, 'object'>> | |
| | Test<Same<Typeof<Error | (() => void)>, 'function' | 'object'>> | |
| | Test<Same<Typeof<Error>, 'object'>> | |
| | Test<Same<Typeof<Exclude<unknown, null | undefined>>, TypeofString>> | |
| | Test<Same<Typeof<Function | { a: 1 }>, 'object' | 'function'>> | |
| | Test<Same<Typeof<Function | string>, 'string' | 'object' | 'function'>> | |
| | Test<Same<Typeof<InstanceType<new () => Date>>, 'object'>> | |
| | Test<Same<Typeof<keyof { a: 1; b: 2 }>, 'string'>> | |
| | Test<Same<Typeof<keyof any>, 'string' | 'number' | 'symbol'>> | |
| | Test<Same<Typeof<Map<string, number>>, 'object'>> | |
| | Test<Same<Typeof<never | null>, 'object'>> | |
| | Test<Same<Typeof<never | object>, 'object' | 'function'>> | |
| | Test<Same<Typeof<never | string>, 'string'>> | |
| | Test<Same<Typeof<never>, never>> | |
| | Test<Same<Typeof<new () => {}>, 'function'>> | |
| | Test<Same<Typeof<NonNullable<unknown>>, TypeofStringDefined>> | |
| | Test<Same<Typeof<null>, 'object'>> | |
| | Test<Same<Typeof<number>, 'number'>> | |
| | Test<Same<Typeof<Number>, 'number' | 'object'>> | |
| | Test<Same<Typeof<object>, 'object' | 'function'>> // that's not confusing at all. | |
| | Test<Same<Typeof<object & (() => void)>, 'function'>> | |
| | Test<Same<Typeof<object & { a: 1 }>, 'object'>> | |
| | Test<Same<Typeof<object | string>, 'object' | 'function' | 'string'>> | |
| | Test<Same<Typeof<object | undefined>, 'object' | 'function' | 'undefined'>> | |
| | Test<Same<Typeof<object>, 'object' | 'function'>> | |
| | Test<Same<Typeof<Omit<{ a: 1; b: 2; c: 3 }, 'b'>>, 'object'>> | |
| | Test<Same<Typeof<Parameters<(x: number, y: string) => void>>, 'object'>> | |
| | Test<Same<Typeof<Partial<{ a: 1; b: 2 }>>, TypeofStringDefined>> | |
| | Test<Same<Typeof<Pick<{ a: 1; b: 2; c: 3 }, 'a' | 'c'>>, 'object'>> | |
| | Test<Same<Typeof<Promise<string>>, 'object'>> | |
| | Test<Same<Typeof<readonly [1, 2, 3]>, 'object'>> | |
| | Test<Same<Typeof<Record<'a' | 'b', number>>, 'object'>> | |
| | Test<Same<Typeof<Record<PropertyKey, never>>, 'object'>> | |
| | Test<Same<Typeof<Record<string, never> | string>, 'object' | 'string'>> | |
| | Test<Same<Typeof<Record<string, never>>, 'object'>> | |
| | Test<Same<Typeof<Record<string, string | number>>, 'object'>> | |
| | Test<Same<Typeof<RegExp>, 'object'>> | |
| | Test<Same<Typeof<Required<{ a?: 1; b?: 2 }>>, 'object'>> | |
| | Test<Same<Typeof<ReturnType<() => { a: 1 }>>, 'object'>> | |
| | Test<Same<Typeof<ReturnType<() => string>>, 'string'>> | |
| | Test<Same<Typeof<Set<number>>, 'object'>> | |
| | Test<Same<Typeof<string & { __brand: 'x' }>, 'string'|'object'>> | |
| | Test<Same<Typeof<string | {}>, TypeofStringDefined>> | |
| | Test<Same<Typeof<string | number | boolean>, 'string' | 'number' | 'boolean'>> | |
| | Test<Same<Typeof<String>, 'string' | 'object'>> | |
| | Test<Same<Typeof<string>, 'string'>> | |
| | Test<Same<Typeof<Symbol>, 'symbol'|'object'>> | |
| | Test<Same<Typeof<symbol>, 'symbol'>> | |
| | Test<Same<Typeof<true | false>, 'boolean'>> | |
| | Test<Same<Typeof<typeof BigInt>, 'function'>> | |
| | Test<Same<Typeof<typeof Boolean>, 'function'>> | |
| | Test<Same<Typeof<typeof Date>, 'function'>> | |
| | Test<Same<Typeof<typeof Error>, 'function'>> | |
| | Test<Same<Typeof<typeof HTMLElement | null>, 'object' | 'function'>> | |
| | Test<Same<Typeof<typeof Number | number>, 'function' | 'number'>> | |
| | Test<Same<Typeof<typeof Number>, 'function'>> | |
| | Test<Same<Typeof<typeof RegExp>, 'function'>> | |
| | Test<Same<Typeof<typeof String | string>, 'function' | 'string'>> | |
| | Test<Same<Typeof<typeof Symbol>, 'function'>> | |
| | Test<Same<Typeof<undefined>, 'undefined'>> | |
| | Test<Same<Typeof<unknown & {}>, TypeofStringDefined>> | |
| | Test<Same<Typeof<unknown>, TypeofString>> | |
| | Test<Same<Typeof<void>, TypeofString>> | |
| | Test<Same<Typeof<Object>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{ length: number }>, 'string' | 'object' | 'function'>> | |
| | Test<Same<Typeof<{ toString(): string }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{ valueOf(): unknown }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<{ valueOf(): string }>, 'object' | 'string'>> | |
| | Test<Same<Typeof<{ valueOf(): String }>, 'object' | 'string'>> | |
| | Test<Same<Typeof<{ valueOf(): symbol }>, 'object' | 'symbol'>> | |
| | Test<Same<Typeof<{ valueOf(): Symbol }>, 'object' | 'symbol'>> | |
| | Test<Same<Typeof<{ valueOf(): boolean }>, 'object' | 'boolean'>> | |
| | Test<Same<Typeof<{ valueOf(): Boolean }>, 'object' | 'boolean'>> | |
| | Test<Same<Typeof<{ valueOf(): number }>, 'object' | 'number'>> | |
| | Test<Same<Typeof<{ valueOf(): Number }>, 'object' | 'number'>> | |
| | Test<Same<Typeof<{ valueOf(): bigint }>, 'object' | 'bigint'>> | |
| | Test<Same<Typeof<{ valueOf(): BigInt }>, 'object' | 'bigint'>> | |
| | Test<Same<Typeof<{ valueOf(): bigint | number }>, 'bigint' | 'number' | 'object'>> | |
| | Test<Same<Typeof<{ valueOf(): object }>, 'object' | 'function'>> | |
| | Test<Same<Typeof<{ (): void; name: unknown }>, 'function'>> | |
| | Test<Same<Typeof<{ (): void }>, 'function'>> | |
| | Test<Same<Typeof<{ x?: number, toString(): string }>, TypeofStringDefined>> | |
| | Test<Same<Typeof<Typeof<unknown>>, 'string'>> | |
| ; // prettier-ignore | |
| type Test<T extends true> = T; | |
| type Same<Actual, Expected> = [Actual] extends [Expected] ? ([Expected] extends [Actual] ? true : false) : false; | |
| /* type k0 = unknown; | |
| type k1 = {}; // all | |
| type k2 = object; // object|function | |
| type k3 = Record<string, never>; // object | |
| type k4 = { [k: string]: never }; // object | |
| type k5 = (...args: any[]) => any; | |
| type k6 = Function; | |
| type k7 = never; | |
| type k8 = { [K in never]: never }; | |
| type k9 = Object; | |
| type k10 = void; */ | |
| /* | |
| ## Types | |
| | # | type | keyof | expected typeof | | |
| | --- | ------------------------- | ------------------ | ------------------------ | | |
| | 0 | `unknown` | `never` | `TypeofReturn` | | |
| | 1 | `{}` | `never` | `TypeofReturnDefined` | | |
| | 9 | `Object` | (large key union) | `TypeofReturnDefined` | | |
| | 2 | `object` | `never` | `'object' \| 'function'` | | |
| | 5 | `(...args: any[]) => any` | `never` | `'function'` | | |
| | 6 | `Function` | (large key union) | `'function'` | | |
| | 3 | `Record<string, never>` | `string` | `'object'` | | |
| | 4 | `{ [k: string]: never }` | `string \| number` | `'object'` | | |
| | 8 | `{ [K in never]: never }` | `never` | `TypeofReturnDefined` | | |
| | 7 | `never` | `never` | `never` | | |
| | 10 | `void` | `never` | `TypeofReturn` | | |
| ## Extends (row extends column) | |
| | extends ↓ / → | unknown (0) | void (9) | {} (1) | Object (9) | object (2) | fn (5) | Function (6) | Rec (3) | Idx (4) | {}₀ (8) | never (7) | void | |
| | ------------- | ----------- | -------- | ------ | ---------- | ---------- | ------ | ------------ | ------- | ------- | ------- | --------- | | |
| | unknown (0) | x | x | | | | | | | | | | | |
| | void (10) | x | x | | | | | | | | | | | |
| | {} (1) | x | | x | x | | | | | | x | | | |
| | Object (9) | x | | x | x | x | | | | | x | | | |
| | object (2) | x | | x | x | x | | | | | x | | | |
| | fn (5) | x | | x | x | x | x | x | | | x | | | |
| | Function (6) | x | | x | x | x | | x | | | x | | | |
| | Rec (3) | x | | x | x | x | | | x | x | x | | | |
| | Idx (4) | x | | x | x | x | | | x | x | x | | | |
| | {}₀ (8) | x | | x | x | x | | | | | x | | | |
| | never (7) | x | x | x | x | x | x | x | x | x | x | x | | |
| ### Legend | |
| - `fn (5)` = `(...args: any[]) => any` | |
| - `Rec (3)` = `Record<string, never>` | |
| - `Idx (4)` = `{ [k: string]: never }` | |
| - `{ }₀ (8)` = `{ [K in never]: never }` (alias of `{}`) | |
| */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export type Typeof<T> = void extends T // explicitly treat void as a top type to account for lambda returns | |
| ? 'object' | 'function' | 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | |
| : TypeofType<T, undefined, 'undefined'> | TypeofType<T, null, 'object'> | TypeofImpl<T & {}>; | |
| type TypeofImpl<T> = T extends ((...args: never) => unknown) | (abstract new (...args: never) => unknown) | |
| ? 'function' | |
| : | |
| | TypeofType<T, string, 'string'> | |
| | TypeofType<T, boolean, 'boolean'> | |
| | TypeofType<T, number, 'number'> | |
| | TypeofType<T, symbol, 'symbol'> | |
| | TypeofType<T, bigint, 'bigint'> | |
| | TypeofType<T, ((...args: never) => unknown) | (abstract new (...args: never) => unknown), 'function'> | |
| | TypeofType<T, object, 'object'>; | |
| type TypeofType<A, B, S> = A extends B ? S : B extends A ? S : B extends OmitOptional<A> ? S : never; | |
| type OmitOptional<T> = T extends object | |
| ? { [P in keyof T as Pick<T, P> extends Required<Pick<T, P>> ? P : never]: T[P] } | |
| : T; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment