Last active
November 8, 2018 15:29
Revisions
-
donabrams revised this gist
Nov 8, 2018 . 1 changed file with 59 additions and 59 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -8,75 +8,75 @@ type SimpleOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; type Foo = { foo: string }; type FooBar = { foo: string, bar: string }; type Yay = { yay: true }; type NonDiscriminatedUnion = 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 DiscriminatedUnion = DFoo | DFooBar | DYay; type MappedAdd<T, K extends string | number | symbol, V> = T & { [P in K]: V }; type MappedOmit<T, K extends string | number | symbol> = T extends { [P in K]: infer U } ? SimpleOmit<T, K> : T; // sanity checks on the NonDiscriminatedUnion type const sanityCheckWorks1: NonDiscriminatedUnion = { foo: 'foo'}; const sanityCheckWorks2: NonDiscriminatedUnion = { foo: 'foo', bar: 'bar'}; const sanityCheckWorks3: NonDiscriminatedUnion = { yay: true}; const sanityCheckFail1: NonDiscriminatedUnion = { }; const sanityCheckFail2: NonDiscriminatedUnion = { foo: 'foo', yay: true }; // FIXME: False positive! const sanityCheckFail3: NonDiscriminatedUnion = { bar: 'bar', yay: true }; // FIXME: False positive! const sanityCheckFail4: NonDiscriminatedUnion = { foo: 'foo', bar: 'bar', yay: true }; // FIXME: False positive! // sanity checks on the Discriminated NonDiscriminatedUnion type const dsanityCheckWorks1: DiscriminatedUnion = { type: 'foo', foo: 'foo' }; const dsanityCheckWorks2: DiscriminatedUnion = { type: 'foobar', foo: 'foo', bar: 'bar' }; const dsanityCheckWorks3: DiscriminatedUnion = { type: 'yay', yay: true }; const dsanityCheckFail1: DiscriminatedUnion = { type: 'foo' }; const dsanityCheckFail2: DiscriminatedUnion = { type: 'foobar' }; const dsanityCheckFail3: DiscriminatedUnion = { type: 'yay' }; const dsanityCheckFail4: DiscriminatedUnion = { type: 'foo', foo: 'foo', bar: 'bar', yay: true }; const dsanityCheckFail5: DiscriminatedUnion = { type: 'foobar', foo: 'foo', bar: 'bar', yay: true }; const dsanityCheckFail6: DiscriminatedUnion = { type: 'yay', foo: 'foo', bar: 'bar', yay: true }; // positive and negative checks on MappedAdd<NonDiscriminatedUnion const addKeyWorks1: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { foo: 'foo', beep: 'beep' }; const addKeyWorks2: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { foo: 'foo', bar: 'bar', beep: 'beep' }; const addKeyWorks3: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { yay: true, beep: 'beep' }; const addKeyWorks4: MappedAdd<NonDiscriminatedUnion, 'foo', string> = { foo: 'foo' }; const addKeyWorks5: MappedAdd<NonDiscriminatedUnion, 'foo', string> = { foo: 'foo', bar: 'bar' }; const addKeyWorks6: MappedAdd<NonDiscriminatedUnion, 'foo', string> = { foo: 'foo', yay: true }; const addKeyFails1: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { foo: 'foo' }; const addKeyFails2: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { foo: 'foo', bar: 'bar' }; const addKeyFails3: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { yay: true }; const addKeyFails4: MappedAdd<NonDiscriminatedUnion, 'foo', string> = { yay: true }; const addKeyFails5: MappedAdd<NonDiscriminatedUnion, 'beep', string> = { foo: 'foo', bar: 'bar', beep: 'beep', yay: true }; // FIXME: False positive! // positive and negative checks on MappedAdd<DiscriminatedUnion const daddKeyWorks1: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'foo', foo: 'foo', beep: 'beep' }; const daddKeyWorks2: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'foobar', foo: 'foo', bar: 'bar', beep: 'beep' }; const daddKeyWorks3: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'yay', yay: true, beep: 'beep' }; const daddKeyWorks4: MappedAdd<DiscriminatedUnion, 'foo', string> = { type: 'foo', foo: 'foo' }; const daddKeyWorks5: MappedAdd<DiscriminatedUnion, 'foo', string> = { type: 'foobar', foo: 'foo', bar: 'bar' }; const daddKeyWorks6: MappedAdd<DiscriminatedUnion, 'foo', string> = { type: 'yay', foo: 'foo', yay: true }; const daddKeyFails1: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'foo', foo: 'foo' }; const daddKeyFails2: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'foobar', foo: 'foo', bar: 'bar' }; const daddKeyFails3: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'yay', yay: true }; const daddKeyFails4: MappedAdd<DiscriminatedUnion, 'foo', string> = { type: 'yay', yay: true }; const daddKeyFails5: MappedAdd<DiscriminatedUnion, 'beep', string> = { type: 'yay', foo: 'foo', bar: 'bar', beep: 'beep', yay: true }; // positive and negative checks on MappedOmit<NonDiscriminatedUnion const removeKeyWorks1: MappedOmit<NonDiscriminatedUnion, 'foo'> = { }; const removeKeyWorks2: MappedOmit<NonDiscriminatedUnion, 'foo'> = { bar: 'bar' }; const removeKeyWorks3: MappedOmit<NonDiscriminatedUnion, 'foo'> = { yay: true }; const removeKeyFails1: MappedOmit<NonDiscriminatedUnion, 'foo'> = { foo: 'foo' }; // FIXME: False positive! const removeKeyFails2: MappedOmit<NonDiscriminatedUnion, 'foo'> = { foo: 'foo', bar: 'bar' }; // FIXME: False positive! // positive and negative checks on MappedOmit<DiscriminatedUnion const dremoveKeyWorks1: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'foo' }; const dremoveKeyWorks2: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'foobar', bar: 'bar' }; const dremoveKeyWorks3: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'yay', yay: true }; const dremoveKeyFails1: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'foo', foo: 'foo' }; const dremoveKeyFails2: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'foobar', foo: 'foo', bar: 'bar' }; const dremoveKeyFails3: MappedOmit<DiscriminatedUnion, 'foo'> = { type: 'yay', yay: true, foo: 'foo'}; -
donabrams revised this gist
Nov 8, 2018 . No changes.There are no files selected for viewing
-
donabrams revised this gist
Nov 8, 2018 . 1 changed file with 11 additions and 0 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,11 @@ { "compilerOptions": { "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "strict": true, /* Enable all strict type-checking options. */ "noUnusedParameters": true, /* Report errors on unused parameters. */ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ } } -
donabrams revised this gist
Nov 8, 2018 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -51,6 +51,7 @@ 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 }; const addKeyFails5: MappedAddKey<Before, 'beep', string> = { foo: 'foo', bar: 'bar', beep: 'beep', yay: true }; // FIXME: False positive! // positive and negative checks on MappedAddKey<DBefore const daddKeyWorks1: MappedAddKey<DBefore, 'beep', string> = { type: 'foo', foo: 'foo', beep: 'beep' }; @@ -63,6 +64,7 @@ const daddKeyFails1: MappedAddKey<DBefore, 'beep', string> = { type: '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 }; const daddKeyFails5: MappedAddKey<DBefore, 'beep', string> = { type: 'yay', foo: 'foo', bar: 'bar', beep: 'beep', yay: true }; // positive and negative checks on MappedRemoveKey<Before const removeKeyWorks1: MappedRemoveKey<Before, 'foo'> = { }; -
donabrams revised this gist
Nov 8, 2018 . 1 changed file with 78 additions and 28 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,30 +1,80 @@ // 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'}; -
donabrams revised this gist
Nov 8, 2018 . 1 changed file with 30 additions and 62 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,62 +1,30 @@ // This doesn't support union types, so we have more shannanigans to do type SimpleOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; // example union type for fun type Before = { foo: string } | { foo: string, bar: string } | { yay: true }; type UnaryObject<K extends string | number | symbol, V> = { [P in K]: V; } const unaryWorks: UnaryObject<'beep', string> = { beep: 'beep' }; const unaryFails1: UnaryObject<'beep', string> = { beep: true }; const unaryFails2: UnaryObject<'beep', string> = { boop: 'beep' }; type AddKey<T, K extends string | number | symbol, V> = T & { [P in K]: V }; const addKeyWorks1: AddKey<Before, 'beep', string> = { foo: 'foo', beep: 'beep' }; const addKeyWorks2: AddKey<Before, 'beep', string> = { foo: 'foo', bar: 'bar', beep: 'beep' }; const addKeyWorks3: AddKey<Before, 'beep', string> = { yay: true, beep: 'beep' }; const addKeyWorks4: AddKey<Before, 'foo', string> = { foo: 'foo' }; const addKeyWorks5: AddKey<Before, 'foo', string> = { foo: 'foo', bar: 'bar' }; const addKeyWorks6: AddKey<Before, 'foo', string> = { foo: 'foo', yay: true }; const addKeyFails1: AddKey<Before, 'beep', string> = { foo: 'foo' }; const addKeyFails2: AddKey<Before, 'beep', string> = { foo: 'foo', bar: 'bar' }; const addKeyFails3: AddKey<Before, 'beep', string> = { yay: true }; const addKeyFails4: AddKey<Before, 'foo', string> = { yay: true }; // I WANT THIS type RemoveKey<T, K extends string | number | symbol> = ??? const removeKeyWorks1: RemoveKey<Before, 'foo'> = { }; const removeKeyWorks2: RemoveKey<Before, 'foo'> = { bar: 'bar' }; const removeKeyWorks3: RemoveKey<Before, 'foo'> = { yay: true }; const removeKeyFails1: RemoveKey<Before, 'foo'> = { foo: 'foo' }; const removeKeyFails2: RemoveKey<Before, 'foo'> = { foo: 'foo', bar: 'bar' }; -
donabrams created this gist
Nov 7, 2018 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,62 @@ // type-safe omit export function omit<T extends Object, K extends string | number | symbol>(obj: T, k: K): Omit<T, K> { const a = { ...(obj as Object) } as T; if (k in a) { delete (a as any)[k]; } return a as Omit<T, K>; } type A = { foo: string, bar: number, }; type B = { foo: string, beep: string }; type C = { bar: number, boop: string, }; type D = A | C; // correctly works function shouldWork(a: A): B { const b: { foo: string } = omit(a, 'bar'); return { ...b, beep: a.bar.toString(), }; } // correctly fails function shouldFail1(a: A): B { const b: { foo: string, bar: number } = omit(a, 'bar'); return { ...b, beep: a.bar.toString(), }; } // correctly fails function shouldFail2(a: A): B { return { ...a, beep: a.bar.toString(), }; } // doesn't work but should :( function shouldWork2(d: D): B | undefined { const b: { foo: string } | { boop: string } = omit(a, 'bar'); return 'foo' in b ? { ...b, beep: a.bar, } : undefined; }