My current "working" approach is this:
const generateMainOrientations = <T extends readonly string[]>(
mainOrientationsNames: T
): { [Index in keyof T]: Orientation } => {
const temp: Orientation[] = mainOrientationsNames.map(
mainOrientationName => ({
name: mainOrientationName,
getYear(date) {
return date.getFullYear()
},
getRecordContent: getMainOrientationRecordContent
})
)
return temp as unknown as { [Index in keyof T]: Orientation }
}
const mainOrientations = generateMainOrientations([
"One",
"Two",
"Three"
] as const)
However, I have to use as unknown as { [Index in keyof T]: Orientation }
, which is not ideal, otherwise (even removing the type assertion from the temp
variable) it will throw
Type '{ name: string; getYear(date: any): any; getRecordContent: (values: number[]) => string[]; }[]' is not assignable to type '{ [Index in keyof T]: Orientation; }'.ts(2322)
Still, { name: string; getYear(date: any): any; getRecordContent: (values: number[]) => string[]; }
is the definition of Orientation
This shows that any length information is lost after map is used.
Is there a more organic way to achieve this, preferably without having to use type assertions at all, or at least without having to use as unknown
. The objective would be to make mainOrientations a tuple of Orientation
of the same length as the argument passed to generateMainOrientations
, so [Orientation, Orientation, Orientation]
in this case, (not Orientation[]
).
CodePudding user response:
You can use temp as any
const generateMainOrientations = <T extends readonly string[]>(
mainOrientationsNames: T
): { [Index in keyof T]: Orientation } => {
const temp = mainOrientationsNames.map((mainOrientationName) => ({
name: mainOrientationName,
getYear(date: Date) {
return date.getFullYear();
},
getRecordContent: getMainOrientationRecordContent
}));
return temp as any;
};
CodePudding user response:
You need to overload your function:
interface Orientation {
name: string,
getYear(date: Date): number,
getRecordContent(values: number[]): string[]
}
declare function getMainOrientationRecordContent(values: number[]): string[]
function generateMainOrientations<T extends string, Tuple extends T[]>(
mainOrientationsNames: [...Tuple]
): { [Index in keyof Tuple]: Orientation }
function generateMainOrientations(
mainOrientationsNames: string[]
) {
return mainOrientationsNames.map(
mainOrientationName => ({
name: mainOrientationName,
getYear: (date: Date) => date.getFullYear(),
getRecordContent: getMainOrientationRecordContent
})
)
}
// [Orientation, Orientation, Orientation]
const mainOrientations = generateMainOrientations([
"One",
"Two",
"Three"
])
Please keep in mind, that once you used Array.prototype.map
, typescript don't preserver the length of the result. Here you can find why.
hence, you have two options: overloading and type assertion.