We need to type the function, which requires a list of keys (strings) and produces object with the same keys where values are string | undefined
when key starts with ?
or otherwise just string
.
export function useParams<T extends string>(
expected: T[]
): { [key in T as NonOptional<T>]: string } {
// ... our custom implementation
return null as any;
}
type NonOptional<T extends string> = T extends `?${infer Inner}` ? Inner : T;
// Should produce type { a: string; b: string | undefined }
const result = useParams(['a', '?b']);
However, I'm not sure how to type the output.
You may write it iterating over tuple's keys:
export function useParams<T extends readonly string[]>(
expected: T
): { [K in T[number] as NonOptional<K>]: string | OrUndefined<K> } {
return null as any;
}
type NonOptional<T extends string> = T extends `?${infer Inner}` ? Inner : T;
type OrUndefined<T extends string> = T extends `?${string}` ? undefined : never
const result = useParams(['a', '?b'] as const);
Added section to address the problem described in comments:
I'm pretty sure developers will forgot to add as const and then it will stop working correctly.
To allow passing non-readonly tuples you may use variadic tuple types:
export function useParams<T extends string[]>(
expected: [...T]
): { [K in T[number] as NonOptional<K>]: string | OrUndefined<K> } {
return null as any;
}
type NonOptional<T extends string> = T extends `?${infer Inner}` ? Inner : T;
type OrUndefined<T extends string> = T extends `?${string}` ? undefined : never
const result = useParams(['a', '?b']);