Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Created September 22, 2019 22:13
Show Gist options
  • Save karol-majewski/f0c0d1986add0b2a8b04cf9fe9506ed8 to your computer and use it in GitHub Desktop.
Save karol-majewski/f0c0d1986add0b2a8b04cf9fe9506ed8 to your computer and use it in GitHub Desktop.
Homogeneous arrays in TypeScript (+ check if a type is a union)
type Singleton = string;
type Union = string | number;
type DistributedKeyOf<T> =
T extends any
? keyof T
: never;
type DistributedValueOf<T> = T[DistributedKeyOf<T>];
type IsSingleton<T> =
unknown extends DistributedValueOf<T>
? false
: true;
type T1 = IsSingleton<Singleton> // $ExpectType true
type T2 = IsSingleton<Union> // $ExpectType false
class HomogeneousArray<T> extends Array<IsSingleton<T> extends true ? T : never> {}
const numbers: HomogeneousArray<number> = [1, 2, 3]; // ✔
const mixed: HomogeneousArray<string | number> = [1, '2', 3]; // ✘
@karol-majewski
Copy link
Author

karol-majewski commented Sep 23, 2019

Another solution based on the fact T | U extends T & U only when T and U are the same type.

/**
 * @see https://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union/53955431
 */
type IsSingleton<T> =
  [T] extends [UnionToIntersection<T>]
    ? true
    : false
type SingletonOnly<T> =
  IsSingleton<T> extends true
    ? T
    : never;

@nekitk
Copy link

nekitk commented Jul 17, 2020

Great gist!

Was looking into homogeneous arrays and found shorter way to define it without checking for a union:

type HomogeneousArray<T extends unknown> = T extends unknown ? T[] : never

const a: HomogeneousArray<number | string> = [1, 2, '3'] // compile-time error

@karol-majewski
Copy link
Author

Nice! Your solution is simpler and gives a better error message, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment