Last active
June 17, 2022 13:49
-
-
Save richsilv/bd1466107a88a35963417ccfc6ee4a69 to your computer and use it in GitHub Desktop.
Revisions
-
richsilv revised this gist
Jun 17, 2022 . 1 changed file with 8 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 @@ -40,6 +40,10 @@ export namespace Thing { bar, } as Thing; } export updateFoo(thing: Thing, newFoo: number) { return Thing.MakeThing(thing.foo, Math.min(newFoo, thing.bar)); } } // A function that requires the new type. @@ -69,6 +73,10 @@ class ThingClass { return new ThingClass(foo, bar); } public updateFoo(newFoo: number) { return new ThingClass(newFoo, Math.min(newFoo, this.bar)); } } // A function that requires the new type. -
richsilv revised this gist
Jun 16, 2022 . 1 changed file with 1 addition and 1 deletion.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 @@ -4,7 +4,7 @@ interface Phantom<T> { } // The idea is that you narrow the type you're going // to use with a property which you're definitely not // going to use, but that distinguishes this from // any old object which matches the same base interface. export type NewType<T, TagT> = T & Phantom<TagT>; -
richsilv revised this gist
Jun 16, 2022 . 1 changed file with 8 additions and 8 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 @@ -48,13 +48,13 @@ function logThing(thing: Thing) { console.log(thing.foo, thing.bar); } logThing({ foo: 10, bar: 5 }); // TYPE ERROR logThing({ foo: 10, bar: 5, __phantom: Symbol() } as const); // TYPE ERROR // There is no way to pass type checking without either calling the // "constructor": logThing(Thing.makeThing(10, 5)); // FINE // Or casting to the new type manually logThing({} as Thing); // FINE :( // **************************************************** @@ -77,6 +77,6 @@ function logThingClass(thing: ThingClass) { console.log(thing.foo, thing.bar); } logThing({ foo: 10, bar: 5 }); // TYPE ERROR logThingClass(ThingClass.makeThing(10, 5)); // FINE logThingClass({} as ThingClass); // FINE :( -
richsilv revised this gist
Jun 16, 2022 . 1 changed file with 4 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 @@ -8,6 +8,8 @@ interface Phantom<T> { // any old object which matches the same base interface. export type NewType<T, TagT> = T & Phantom<TagT>; // **************************************************** // An example base interface: interface ThingBase { readonly foo: number; @@ -54,6 +56,8 @@ logThing(Thing.makeThing(10, 5)); // Or casting to the new type manually :( logThing({} as Thing); // **************************************************** // But note that this is exactly as type-safe as a class: class ThingClass { private constructor(readonly foo: number, readonly bar: number) {} -
richsilv created this gist
Jun 16, 2022 .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,78 @@ // Utility types (required only once): interface Phantom<T> { __phantom: T; } // The idea is that you narrow the type you're going // to use with a property which you're definitely not // going to be used, but that distinguishes this from // any old object which matches the same base interface. export type NewType<T, TagT> = T & Phantom<TagT>; // An example base interface: interface ThingBase { readonly foo: number; readonly bar: number; } // A non-reproduceable type to make your final type // non-reproduceable (this is safer than using a string). // Not exported! const THING_SYMBOL: unique symbol = Symbol(); // Your resultant type. export type Thing = NewType<ThingBase, typeof THING_SYMBOL>; // eslint-disable-next-line @typescript-eslint/no-redeclare export namespace Thing { // A "constructor" for your type, which encodes the // required logic and casts to the new type. // You'd use similar free-functions with casts for // updates, etc. export function makeThing(foo: number, bar: number): Thing { if (foo <= bar) { throw new Error("Foo must be greater than bar!"); } return { foo, bar, } as Thing; } } // A function that requires the new type. function logThing(thing: Thing) { // eslint-disable-next-line no-console console.log(thing.foo, thing.bar); } logThing({ foo: 10, bar: 5 }); logThing({ foo: 10, bar: 5, __phantom: Symbol() } as const); // There is no way to pass type checking without either calling the // "constructor": logThing(Thing.makeThing(10, 5)); // Or casting to the new type manually :( logThing({} as Thing); // But note that this is exactly as type-safe as a class: class ThingClass { private constructor(readonly foo: number, readonly bar: number) {} public static makeThing(foo: number, bar: number): ThingClass { if (foo <= bar) { throw new Error("Foo must be greater than bar!"); } return new ThingClass(foo, bar); } } // A function that requires the new type. function logThingClass(thing: ThingClass) { // eslint-disable-next-line no-console console.log(thing.foo, thing.bar); } logThing({ foo: 10, bar: 5 }); logThingClass(ThingClass.makeThing(10, 5)); logThingClass({} as ThingClass); // :(