Skip to content

Instantly share code, notes, and snippets.

@asktree
Last active February 5, 2021 17:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asktree/8ab244583d4163f09a5a15ba5f60c7a3 to your computer and use it in GitHub Desktop.
Save asktree/8ab244583d4163f09a5a15ba5f60c7a3 to your computer and use it in GitHub Desktop.
TypeScript's type assignment feature destroys information (inconsistently)
// Typescript lets you put constraints on assignment -- cool
// - I don't know what this feature is officially called and can't find it.
// for example, telling the IDE that a constant should be "even"
type even = 2 | 4 | 6;
const a = 2;
const b: even = 2;
const c: even = 4;
const d: even = 3; // error: constraint violation
// you can assert types, this is different!
const d2 = 3 as even; // NO error: true assertion
// Unlike assertion, assignment *can* preserve information
// (but don't get excited)
type prime = 2 | 3 | 5;
const x: prime = a;
const y: prime = b; // does not error, assignment constraint did not destroy information
const z: prime = c; // does error, appropriate
const b2 = 2 as even;
const y2: prime = b2; // does error, assertions do destroy information
// ## Now let's try with readonly objects
const A = { n: 2 } as const;
const X: prime = A.n; // does not error, TS knows `A.n === 2` because it's readonly
const C: { n: even } = { n: 3 } as const; // errors correctly -- the constraint is not an assertion
const B: { n: even } = { n: 2 } as const;
const Y: prime = B.n;
// expected: no error, `B.n` is prime and we didn't assert.
// ❗️ ❗️ ❗️ Actual: does error, assignment constraint DID destroy information!!!
// # ❔ How can I tell the compiler that I want something to be of type `{n: even}` without destroying information?
const D = { n: 3 } as const;
// ## ✅ the lame way (contains vestigial runtime stuff)
const check = <T>(x: T) => {};
check<{ n: even }>(A); // no error
check<{ n: even }>(D); // error, 3 is not even
// ## ✅ the lamer way (more vestigial runtime stuff)
const checkedA: { n: even } = A; // no error
const checkedC: { n: even } = D; // error
// ## ❌ this has no runtime crap but also doesn't work (`CheckC = never` but still compiles)
type CheckA = typeof A & { n: even };
type CheckC = typeof D & { n: even };
// ## ✅ this works and even looks like Type-Driven Development!...
declare let g: even;
g = 3; // error: woo is not 3
// ❌ ... but doesn't work with const
// It seems like my approaches that involve runtime code are the only ways to do it.
// I really dislike that assignment constraints can destroy information.
// The difference between assignment constraints and assertions should be consistent across values.
@asktree
Copy link
Author

asktree commented Feb 5, 2021

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