Last active
March 9, 2019 01:56
-
-
Save rtpg/30f00608af35489e489be17c4bc9caca to your computer and use it in GitHub Desktop.
How to write an assertion that two objects are the same shape in Typescript
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 worked in Typescript 3.2.4 | |
// It feels like we should be able to say SameShape<Q extends R, R extends Q>, but this triggers circular reference | |
// detection in Typescript. Somehow going to {[K in keyof Q]: Q[K]} solves this (though maybe this won't catch things | |
// like symbols?) | |
type SameShape<Q extends R, R extends { [K in keyof Q]: Q[K] }> = { | |
[K in keyof Q]: R[K] // re-asserting every key of Q is present in R with the same type | |
} & { // anding the types together will catch any conflicts | |
[K in keyof R]: Q[K] // re-asserting every key of R is present in Q with the same type | |
} | |
// unfortunately I didn't see a good way of avoiding repeating the generic type here, but I think there _is_ a way | |
// to avoid it with infer or ... something. This isn't superb but it gets the job done and should be limited to | |
// library code | |
function assertShape<Shape extends { [K in keyof Data]: Data[K] }, Data extends Shape>( | |
s: Shape, | |
d: Data | |
): SameShape<Shape, Data> { | |
return d as any; | |
} | |
assertShape({a: 1, b: 2}, {a: 3, b: 4}); | |
// the following will fail at typecheck | |
// missing keys | |
// assertShape( | |
// {a:1, b:2}, | |
// {a:3, b:4, c:5} | |
// ) | |
// extra keys | |
// assertShape( | |
// {a: 1, b:2}, | |
// {a:1}, | |
// ) | |
// type mismatch | |
// assertShape({a:1}, {a: 'hi'}); | |
// type mismatch in inner keys | |
// assertShape({ | |
// a: {a: 3}, | |
// }, {a: {a: 3, b: 4}} | |
//) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment