declare RepeatedNamesDB: { name: string, repeatitions: number}
const my_stats_v1 = {name: "haxxor", game: "league of fools and falafel", fame: 505, tame: false, lame: ["yes", 735]}
const stats_v1_to_v2: RecordMapper<typeof my_stats_v1> = {
name: (s) => {
// `s` is automatically inferred as a `string`, thanks to `typeof my_stats_v1` generic parameter
let rep = RepeatedNamesDB[s]++
return [s, rep]
},
game: (s) => s,
fame: (v) => v * 1.5,
tame: (b) => undefined,
lame: (a) => ({
current_status: a[0] === "yes" ? true : false,
bad_reputation_history: [["pre-v2", a[1]], ["original-sin", 5], ]
})
}
uh oh, did you notice the problem? the IDE thinks that stats_v1_to_v2
maps each entry of my_stats_v1
to unknown
.
you must provide a second type parameter that specifies the new type of each entry (which in this context would be stats_v2
).
// declare RepeatedNamesDB; const my_stats_v1 = {...};
type my_stats_v2 = {
name: [string, number],
game: string,
fame: number,
tame: undefined,
lame: {
current_status: boolean,
bad_reputation_history: Array<[occasion: string, value: number]>
}
}
const stats_v1_to_v2: RecordMapper<typeof my_stats_v1, my_stats_v2> = {
// just as before
}
but this is a lot of repetition in typing, and the additional type will be utterly useless if it's not being used elsewhere.
luckily, with the introduction of the satisfies
operator in tsc 4.9
, you can be far more consise:
declare RepeatedNamesDB: { name: string, repeatitions: number}
const my_stats_v1 = {name: "haxxor", game: "league of fools and falafel", fame: 505, tame: false, lame: ["yes", 735]}
// the map function parameters `s`, `v`, `b`, and `a` all have their types automatically inferred thanks to the `satisfies` operator
// `stats_v1_to_v2` now indeed maps the correct `stats_v2` interface
const stats_v1_to_v2 = {
name: (s) => {
let rep = RepeatedNamesDB[s]++
return [s, rep]
},
game: (s) => s,
fame: (v) => v * 1.5,
tame: (b) => undefined,
lame: (a) => ({
current_status: a[0] === "yes" ? true : false,
bad_reputation_history: [["pre-v2", a[1]], ["original-sin", 5], ]
})
} satisfies RecordMapper<typeof my_stats_v1>
now, for an example that uses the D
default type generic parameter (3rd parameter):
const now_i_know_my = { a: 1, b: 2, c: 3, s: "nein" }
const latin_to_greek: RecordMapper<
typeof now_i_know_my, // these are the inputs that will be mapped
{ s: number }, // entry `"s"` will be mapped to a `number`
string // all other entries will be mapped to `string`
> = {
a: (v) => `${v}-alpha`,
b: (v) => `${v}-beta`,
c: (v) => `${v}-theta`,
s: (v) => 9
}
represents an
Object
consisting of a collection of single-parameter functions that map the entries of typeR
to entries of typeU
however, if
U
does not contain a certain key that's inR
, then we will assume that it is being mapped to a single default typeD
to give you an idea, here is a flawed example: (more is covered on the flaw right after)