Here is a scenario I am working on:
I need to develop a function that maintains the correct data types.
Input
type A = {a: number|undefined, string: b} | string
type B = {a: number|null, string: b} | string
type FnType = (a: {a: number | null, string: b}) => {a: number | undefined, b: string}
type FnType2 = (a: {a: number | undefined, string: b}) => {a: number | null, b: string}
type FnType3 = (a: {c: string | undefined, string: d: number}) => {a: string | null, d: number}
Function implementation
const example = (fn) => (value) => {
if(typeof value === 'string') return value
return fn(value)
}
The result type of the example
function can correspond to either type A or type B depending on whether it's using FnType
or FnType2
. The key point here is that we are only certain that the value could be a string
, and the fn
function itself can vary.
To comprehend this feature better, one can analyze the example function and ensure that the types are inferred correctly.
For instance, when passing a function with the signature
(a: {a: string | undefined}): => { a: string | null }
, the signature of the value
would then be
string | {a: string | undefined}
and the returned type from the example
function would be interpreted as
{a: string | null} | string
If manually defining this process for mapping from A to B, one would follow these steps and repeat in reverse to map back from null
to undefined
:
const mapper = (val: { a: { key1: number | undefined } | string }): { a: { key1: number | null } | string }} => {
return {
a: typeof val.a === 'string' ? val.a :
{
key1: val.a.key1 ?? null
}
}
}