Reputation: 462
I have an app where I use a json structure to define the columns of a table.
Each column has a key
and label
property and an optional transformFunc
property that takes a function that transforms the data to be displayed in the column.
This is the type for Column
:
type Column = {
key: string
label: string
transformFunc?: (value: string | number | Date) => string
};
transformFunc
either takes a string
, number
or Date
as an argument.
However, I'm not able to properly type it this way:
const col: Column = {
key: 'date',
label: 'Date',
transformFunc: (value: string) => value.substring(0, 5) // TS2322: Type '(value: string) => string' is not assignable to type '(value: string | number | Date) => string'
}
How can I solve this?
I looked into generics but I don't see how to do this.
Upvotes: 3
Views: 111
Reputation: 2472
Alternatively if you don't want to use a generic, the union should instead be outside of the function scope
type Column = {
key: string
label: string
transformFunc?:
((value: string) => string) |
((value: number) => string) |
((value: Date) => string)
};
const col: Column = {
key: 'date',
label: 'Date',
transformFunc: (value: string) => value.substring(0, 5)
}
Or if you want to infer the type based on the value of transformFunc you can use a helper function
type ColumnGeneric<T extends string | number | Date> = {
key: string
label: string
transformFunc?: (value: string | number | Date) => string
}
const columnInferenceBuilder = <
_ extends ColumnGeneric<FunctionProp>,
FunctionProp extends string | number | Date
>(o: {
key: string;
label: string;
transformFunc: (value: FunctionProp) => string
}) => o
const inferredColumn = columnInferenceBuilder({
key: 'date',
label: 'Date',
transformFunc: (value: string) => 'foobar',
})
This pattern might be more useful if you had an extra property of value
that corresponds to the transformFunc arguments (or somehow related otherwise to discriminate with)
// imagine its imported
const importedData = {
key: 'foo',
label: 'Foo',
value: 0
} as const
const inferredColumn2 = columnInferenceBuilder2({
...importedData,
transformFunc: (value) => 'foobar',
// ^? (value: 0) => string
})
View these examples on TS Playground
Upvotes: 0
Reputation: 45865
After defining Column
, you could had no need for any type when using it. You just use values, like 'date'
and 'Date'
, give to transformFunc
a function without any type, then use type narrowing to have what you want. Something like the below code.
See this TypeScript playgroud I created.
type Column = {
key: string
label: string
transformFunc?: (value: string | number | Date) => string
};
const col: Column = {
key: 'date',
label: 'Date',
transformFunc: (value) => {if (typeof value == "string") {return value.substring(0, 5)}else return ""}
}
Upvotes: 0
Reputation: 1815
In generic way
type Column<T extends string | number | Date> = {
key: string
label: string
transformFunc?: (value: T) => string
};
const col: Column<string> = {
key: 'date',
label: 'Date',
transformFunc: (value) => value.substring(0, 5)
}
Upvotes: 4
Reputation: 2666
Because of your type definition, a transformFunc
must accept all of the union's type, so you can't create a Column with a transformFunc
accepting just one.
If you know what type of data a specific column will hold, you can use a type assertion in your function:
const col: Column = {
key: 'date',
label: 'Date',
transformFunc: (value: string | number | Date) =>
(value as string).substring(0, 5)
}
Upvotes: 0