I tried to use csv-parse's option cast
to convert the type.
My approach is as follows, but there are problems.
I referred to this answer: https://stackoverflow.com/a/60932900/19252706
Is it to possible to define KeysOfNumbers and KeysOfBooleans:
import {CastingFunction, parse} from 'csv-parse/browser/esm/sync';
const input =
'ID,Type,From,Title,Content,Date,IsRead,IsAD\r\n1,0,Mars,My car glass was broken,How much DOGE to fix this.....,423042301654134900000,false,false';
type Mail = {
ID: string;
Type: number;
From: string;
Title: string;
Content: string;
Date: number;
isRead: boolean;
isAD: boolean;
};
// This is problem. Is this possible to fix?
type KeysOfNumbers<T> = string[];
type KeysOfBooleans<T> = string[];
const castNumberAndBoolean =
<T>(
keysOfNumbers: KeysOfNumbers<T>,
KeysOfBooleans: KeysOfBooleans<T>,
): CastingFunction =>
(value, context) =>
keysOfNumbers.includes(context.column.toString())
? Number(value)
: KeysOfBooleans.includes(context.column.toString())
? value === 'true'
? true
: false
: value;
parse(input, {
columns: true,
cast: castNumberAndBoolean<Mail>(['Type', 'Date'], ['isRead', 'isAD']),
});
This is a handy helper type:
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never
}[keyof T];
type TestA = KeysOfType<Mail, boolean>
// 'isRead' | 'isAD'
This type takes an object to map T
, and a type for the properties to keep U
. It iterates over each key K
and checks if the type of that property T[K]
extends U
. If it does, keep the key, else throw it out by resolving to never
. Lastly index the resulting object by its own keys to get all property types as a union.
Now you can make types that use this type:
type KeysOfNumbers<T> = KeysOfType<T, number>[]
type KeysOfBooleans<T> = KeysOfType<T, boolean>[]
type MailNumbers = KeysOfNumbers<Mail>
// ('Type' | 'Date')[]
type MailBooleans = KeysOfBooleans<Mail>
// ('isRead' | 'isAD')[]