Last active
November 8, 2018 15:29
-
-
Save donabrams/b849927f5a0160081db913e3d52cc7b3 to your computer and use it in GitHub Desktop.
Typesafe omit typescript (TS 3.1.6)
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
// This doesn't support map over union types individually | |
type SimpleOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; | |
// The below type, MappedRemoveKey, only works with discrimated unions! | |
// Otherwise the intersection of the types ALSO passes (submiting a TS bug now-- in typescript-3.1.6) | |
// example union type WITHOUT a discriminator | |
type Foo = { foo: string }; | |
type FooBar = { foo: string, bar: string }; | |
type Yay = { yay: true }; | |
type Before = Foo | FooBar | Yay; | |
// example union type WITH a discriminator | |
type DFoo = { type: 'foo', foo: string }; | |
type DFooBar = { type: 'foobar', foo: string, bar: string }; | |
type DYay = { type: 'yay', yay: true }; | |
type DBefore = DFoo | DFooBar | DYay; | |
type MappedAddKey<T, K extends string | number | symbol, V> = T & { [P in K]: V }; | |
type MappedRemoveKey<T, K extends string | number | symbol> = T extends { [P in K]: infer U } | |
? SimpleOmit<T, K> : T; | |
// sanity checks on the Before type | |
const sanityCheckWorks1: Before = { foo: 'foo'}; | |
const sanityCheckWorks2: Before = { foo: 'foo', bar: 'bar'}; | |
const sanityCheckWorks3: Before = { yay: true}; | |
const sanityCheckFail1: Before = { }; | |
const sanityCheckFail2: Before = { foo: 'foo', yay: true }; // FIXME: False positive! | |
const sanityCheckFail3: Before = { bar: 'bar', yay: true }; // FIXME: False positive! | |
const sanityCheckFail4: Before = { foo: 'foo', bar: 'bar', yay: true }; // FIXME: False positive! | |
// sanity checks on the Discriminated Before type | |
const dsanityCheckWorks1: DBefore = { type: 'foo', foo: 'foo' }; | |
const dsanityCheckWorks2: DBefore = { type: 'foobar', foo: 'foo', bar: 'bar' }; | |
const dsanityCheckWorks3: DBefore = { type: 'yay', yay: true }; | |
const dsanityCheckFail1: DBefore = { type: 'foo' }; | |
const dsanityCheckFail2: DBefore = { type: 'foobar' }; | |
const dsanityCheckFail3: DBefore = { type: 'yay' }; | |
const dsanityCheckFail4: DBefore = { type: 'foo', foo: 'foo', bar: 'bar', yay: true }; | |
const dsanityCheckFail5: DBefore = { type: 'foobar', foo: 'foo', bar: 'bar', yay: true }; | |
const dsanityCheckFail6: DBefore = { type: 'yay', foo: 'foo', bar: 'bar', yay: true }; | |
// positive and negative checks on MappedAddKey<Before | |
const addKeyWorks1: MappedAddKey<Before, 'beep', string> = { foo: 'foo', beep: 'beep' }; | |
const addKeyWorks2: MappedAddKey<Before, 'beep', string> = { foo: 'foo', bar: 'bar', beep: 'beep' }; | |
const addKeyWorks3: MappedAddKey<Before, 'beep', string> = { yay: true, beep: 'beep' }; | |
const addKeyWorks4: MappedAddKey<Before, 'foo', string> = { foo: 'foo' }; | |
const addKeyWorks5: MappedAddKey<Before, 'foo', string> = { foo: 'foo', bar: 'bar' }; | |
const addKeyWorks6: MappedAddKey<Before, 'foo', string> = { foo: 'foo', yay: true }; | |
const addKeyFails1: MappedAddKey<Before, 'beep', string> = { foo: 'foo' }; | |
const addKeyFails2: MappedAddKey<Before, 'beep', string> = { foo: 'foo', bar: 'bar' }; | |
const addKeyFails3: MappedAddKey<Before, 'beep', string> = { yay: true }; | |
const addKeyFails4: MappedAddKey<Before, 'foo', string> = { yay: true }; | |
// positive and negative checks on MappedAddKey<DBefore | |
const daddKeyWorks1: MappedAddKey<DBefore, 'beep', string> = { type: 'foo', foo: 'foo', beep: 'beep' }; | |
const daddKeyWorks2: MappedAddKey<DBefore, 'beep', string> = { type: 'foobar', foo: 'foo', bar: 'bar', beep: 'beep' }; | |
const daddKeyWorks3: MappedAddKey<DBefore, 'beep', string> = { type: 'yay', yay: true, beep: 'beep' }; | |
const daddKeyWorks4: MappedAddKey<DBefore, 'foo', string> = { type: 'foo', foo: 'foo' }; | |
const daddKeyWorks5: MappedAddKey<DBefore, 'foo', string> = { type: 'foobar', foo: 'foo', bar: 'bar' }; | |
const daddKeyWorks6: MappedAddKey<DBefore, 'foo', string> = { type: 'yay', foo: 'foo', yay: true }; | |
const daddKeyFails1: MappedAddKey<DBefore, 'beep', string> = { type: 'foo', foo: 'foo' }; | |
const daddKeyFails2: MappedAddKey<DBefore, 'beep', string> = { type: 'foobar', foo: 'foo', bar: 'bar' }; | |
const daddKeyFails3: MappedAddKey<DBefore, 'beep', string> = { type: 'yay', yay: true }; | |
const daddKeyFails4: MappedAddKey<DBefore, 'foo', string> = { type: 'yay', yay: true }; | |
// positive and negative checks on MappedRemoveKey<Before | |
const removeKeyWorks1: MappedRemoveKey<Before, 'foo'> = { }; | |
const removeKeyWorks2: MappedRemoveKey<Before, 'foo'> = { bar: 'bar' }; | |
const removeKeyWorks3: MappedRemoveKey<Before, 'foo'> = { yay: true }; | |
const removeKeyFails1: MappedRemoveKey<Before, 'foo'> = { foo: 'foo' }; // FIXME: False positive! | |
const removeKeyFails2: MappedRemoveKey<Before, 'foo'> = { foo: 'foo', bar: 'bar' }; // FIXME: False positive! | |
// positive and negative checks on MappedRemoveKey<DBefore | |
const dremoveKeyWorks1: MappedRemoveKey<DBefore, 'foo'> = { type: 'foo' }; | |
const dremoveKeyWorks2: MappedRemoveKey<DBefore, 'foo'> = { type: 'foobar', bar: 'bar' }; | |
const dremoveKeyWorks3: MappedRemoveKey<DBefore, 'foo'> = { type: 'yay', yay: true }; | |
const dremoveKeyFails1: MappedRemoveKey<DBefore, 'foo'> = { type: 'foo', foo: 'foo' }; | |
const dremoveKeyFails2: MappedRemoveKey<DBefore, 'foo'> = { type: 'foobar', foo: 'foo', bar: 'bar' }; | |
const dremoveKeyFails3: MappedRemoveKey<DBefore, 'foo'> = { type: 'yay', yay: true, foo: 'foo'}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment