Last active
April 5, 2019 14:17
-
-
Save webstrand/f32f11c79f383b9abd7942eef60dc50d to your computer and use it in GitHub Desktop.
Demonstration of Excluding<T> which emulates the subtractive type `object - 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
| // Since typescript nominally types string based enums, we emulate | |
| // the subtractive type `object - T` by making all the properties | |
| // T optional and setting their types to a non-exported enum. | |
| // | |
| // The enum name is long and descriptive to help with debugging. | |
| const enum CannotAssignToPropertyFromExcludedObject { ϕ = "ϕ" } | |
| type Excluding<T extends object> = { [P in keyof T]?: CannotAssignToPropertyFromExcludedObject; } & { [prop: string]: any; } | |
| // Note: If some overlap is acceptable, providing that the overlapping property extends T[P], use | |
| // type Excluding<T extends object> = Partial<T> & { [prop: string]: any }; | |
| // Class implementation of Foo<T> | |
| class FooImpl<T extends object> { | |
| fooProp!: number; | |
| protected anotherFooProp!: number; | |
| constructor() { | |
| } | |
| } | |
| // Here we use Excluding<T> to create a Foo<T> which represents | |
| // the type of FooImpl & T, while guaranteeing that T does not | |
| // overlap with any of FooImpl's underlying properties. | |
| type Foo<T extends Excluding<FooImpl<{}>>> = FooImpl<T> & T; | |
| const Foo: { | |
| prototype: Foo<any> | |
| new <T extends Excluding<FooImpl<{}>>>(): Foo<T> | |
| } = FooImpl as any; | |
| // no error, "a" and "b" do not overlap with any property | |
| // of FooImpl. | |
| const ex1 = new Foo<{a: string, b: string}> (); | |
| console.log ( ex1.a ); // 123 | |
| console.log ( ex1.b ); // 'asd' | |
| // error, "fooProp" overlaps with a property of FooImpl, | |
| // and is therefore dangerous to allow intersection of | |
| // T & FooImpl. | |
| const ex2 = new Foo<{a: string, fooProp: string}>(); | |
| // Private and protected properties aren't supported, though, so | |
| // be careful. | |
| const ex3 = new Foo<{a: string, anotherFooProp: string}>(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment