Reputation: 6527
I have a function I want to convert to generics Typescript to provide as an utility. I have reached it but what happens when a function returns a function. I know it is not required but still
/**
* createConstants Type
*/
type createConstantsType =
<T extends string, U extends string>(namespace: T, prefix: U | null) =>
<V extends string>(...constants: V[]) => Record<V, string>;
/**
* function for creating namespaced constants
* @param {String} namespace the namespace for the constant to create
* @param {String} prefix the prefix for the constant to create
* @returns {Object} namespaced constants for a module/feature
*
* // Common approach
* export const NAMESPACE = 'manual';
* export const SIDEBAR_TOGGLE = `${NAMESPACE}/SIDEBAR_TOGGLE`;
* export const SIDEBAR_OPEN = `${NAMESPACE}/SIDEBAR_OPEN`;
* export const SIDEBAR_CLOSE = `${NAMESPACE}/SIDEBAR_CLOSE`;
*
* // Usage of this utility
* export const NAMESPACE = 'manual';
* export const SIDEBAR = createConstants(NAMESPACE, 'sidebar')('TOGGLE', 'OPEN', 'CLOSE');
*
* // which will generate:
* SIDEBAR = {
* TOGGLE: 'manual/SIDEBAR_TOGGLE',
* OPEN: 'manual/SIDEBAR_OPEN',
* CLOSE: 'manual/SIDEBAR_CLOSE',
* }
*
*/
export const createConstants: createConstantsType =
<T extends string, U extends string>(namespace: T, prefix: U | null = null) =>
<V extends string>(...constants: V[]): Record<V, string> => (
constants.reduce((result: Record<V, string>, constant: string): Record<V, string> => ({
[constant.toUpperCase()]:
`${namespace}/${(prefix) ? `${prefix.toUpperCase()}_` : ''}${constant.toUpperCase()}`,
...result,
}), {} as Record<V, string>)
);
Upvotes: 0
Views: 239
Reputation: 25860
There are a few things to be aware of:
String
when what you mean is string
. The uppercase one is a reference to the global String
object in JavaScript.Function
. The definition of Function
is very broad, and even if your function makes use of some precise types and generics, TypeScript will ignore all of that because it's told to treat it as just any Function
. If you want to create a function type, write its signature this way: type MyFunction = (argument: number) => boolean
instead.string
, you can start just by creating a type parameter (a generic) and saying it extends string
. So, <T extends string>(argument: T)
instead of (argument: string)
.When you apply these rules, your solution will look like this:
export const createConstants = <T extends string, U extends string>(namespace: T, prefix: U | null = null) => <V extends string>(...constants: V[]): Record<V, string> => (
constants.reduce((result, constant) => ({
[constant.toUpperCase()]:
`${namespace}/${(prefix) ? `${prefix.toUpperCase()}_` : ''}${constant.toUpperCase()}`,
...result,
}), {} as Record<V, string>)
);
const test = createConstants('manual', 'SIDEBAR')('TOGGLE', 'OPEN', 'CLOSE')
Upvotes: 2