Skip to content

Instantly share code, notes, and snippets.

@hasparus
Last active March 8, 2019 10:08
Show Gist options
  • Save hasparus/72d9ff63dc8f5dd958a39f1614b31b56 to your computer and use it in GitHub Desktop.
Save hasparus/72d9ff63dc8f5dd958a39f1614b31b56 to your computer and use it in GitHub Desktop.
TypeScript Playground -- Merging types
// Paste this to typescriptlang.org/play or click the link the comments
type Expected = {
a: 1 | 2
b?: 1 | 2
c?: 1
d?: 1 | 2
e: 2
f?: 2
}
type TXY = {
a: 1
b: 1
c?: 1
d?: 1
e?: 1
};
type TXX = {
a: 2
b?: 2
d?: 2
e: 2
f: 2
};
type IntegerKeys<T extends Array<any>> = Exclude<keyof T, keyof typeof Array.prototype>;
// Step by step
type Arg = [TXY, TXX];
type AllKeys = keyof UnionToIntersection<Arg[number]>;
type WithUndefined = {
[I in IntegerKeys<Arg>]: { [P in AllKeys]: P extends keyof Arg[I] ? Arg[I][P] : undefined }
}
type X = Simplify<WithUndefined[keyof WithUndefined]>;
// The Simplify up here is not mandatory, added for better tooltip
// Merged types in Arg
type Result = Simplify<{ [P in OptionalKeys<X>]?: X[P] } & { [P in RequiredKeys<X>]: X[P] }>
assertType<IsExtends<Expected, Result>>();
assertType<IsExtends<Result, Expected>>(); // Dunno why, it looks the same to me
const xs: Result[] = [
{ a: 2 }, { b: 1, a: 2, f: undefined }, { f: 2, a: 2 },
]
type PickKeys<T extends object, TValue> = Exclude<
{ [K in keyof T]: TValue extends T[K] ? K : never }[keyof T], undefined
>;
type NullableKeys<T extends object> = PickKeys<T, null>
type OptionalKeys<T extends object> = PickKeys<T, undefined>
type RequiredKeys<T extends object> = Exclude<keyof T, OptionalKeys<T>>
type Simplify<T> = Pick<T, keyof T>
// from typical-ts
type UnionToIntersection<U> = (
U extends any ? (k: U) => void : never
) extends ((k: infer I) => void) ? I : never;
// from typepark
type And<A extends boolean, B extends boolean> = A extends true ? (B extends true ? true : false) : false;
type Or<A extends boolean, B extends boolean> = A extends true ? true : (B extends true ? true : false);
type Xor<A extends boolean, B extends boolean> = A extends true ? (B extends true ? false : true) : (B extends true ? true : false);
type Not<X extends boolean> = X extends true ? false : true;
type IsExtends<A, B> = A extends B ? true : false;
type TypeEqNotUnion<A, B> = And<IsExtends<A, B>, IsExtends<B, A>>;
type ComparableType<T> = [T];
type TypeEqNotAny<A, B> = TypeEqNotUnion<ComparableType<A>, ComparableType<B>>;
type IsAny<T> = And<TypeEqNotAny<T, 1>, TypeEqNotAny<T, 2>>;
type IsNotAny<T> = Not<IsAny<T>>;
type TypeEq<A, B> = Or<And<IsAny<A>, IsAny<B>>, And<And<IsNotAny<A>, IsNotAny<B>>, TypeEqNotAny<A, B>>>;
function assertType<_T extends true>() { }
@hasparus
Copy link
Author

hasparus commented Mar 8, 2019

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