Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions packages/csv-parse/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ export type ColumnOption<K = string> =
| false
| { name: K };

type ColumnKey<T> = T extends string[]
? string
: unknown extends T
? string
: string | keyof T;

// Keep columns from overriding record types inferred from options such as raw.
type NoInferColumnRecord<T> = [T][T extends unknown ? 0 : never];

export interface OptionDelimiterAuto {
preferred: Record<string, number>;
score: () => number;
Expand Down Expand Up @@ -120,21 +129,17 @@ export interface OptionsNormalized<T = string[], U = T> {
* Internal property string the function to
*/
cast_first_line_to_header?: (
record: T,
) => ColumnOption<
T extends string[] ? string : T extends unknown ? string : keyof T
>[];
record: string[],
) => ColumnOption<ColumnKey<U>>[];
/**
* List of fields as an array, a user defined callback accepting the first
* line and returning the column names or true if autodiscovered in the first
* CSV line, default to null, affect the result data set in the sense that
* records will be objects instead of arrays.
* records will be objects instead of arrays. The callback receives the raw
* header fields as strings, while returned names may use keys from the typed
* input record.
*/
columns:
| boolean
| ColumnOption<
T extends string[] ? string : T extends unknown ? string : keyof T
>[];
columns: boolean | ColumnOption<ColumnKey<U>>[];
/**
* Treat all the characters after this one as a comment, default to '' (disabled).
*/
Expand Down Expand Up @@ -305,15 +310,11 @@ export interface Options<T = string[], U = T> {
* List of fields as an array,
* a user defined callback accepting the first line and returning the column names or true if autodiscovered in the first CSV line,
* default to null,
* affect the result data set in the sense that records will be objects instead of arrays.
* affect the result data set in the sense that records will be objects instead of arrays. The callback receives the raw header fields as strings, while returned names may use keys from the typed input record.
*/
columns?:
| OptionsNormalized["columns"]
| ((
record: T,
) => ColumnOption<
T extends string[] ? string : T extends unknown ? string : keyof T
>[]);
| OptionsNormalized<T, U>["columns"]
| ((record: string[]) => ColumnOption<ColumnKey<U>>[]);
/**
* Treat all the characters after this one as a comment, default to '' (disabled).
*/
Expand Down Expand Up @@ -510,7 +511,10 @@ export class CsvError extends Error {
}

export type OptionsWithColumns<T, U = T> = Omit<Options<T, U>, "columns"> & {
columns: Exclude<Options["columns"], undefined | false>;
columns: Exclude<
Options<NoInferColumnRecord<T>, NoInferColumnRecord<U>>["columns"],
undefined | false
>;
};

declare function parse<T = unknown, U = T>(
Expand Down
11 changes: 11 additions & 0 deletions packages/csv-parse/test/api.types.sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,16 @@ describe("API Types", function () {
});
data;
});

it("Accepts typed keys from a columns callback", function () {
const columnMapping = new Map<string, keyof Person>([
["full_name", "name"],
["years", "age"],
]);
const data: Person[] = parse<Person>("full_name,years\nAda,36", {
columns: (header) => header.map((column) => columnMapping.get(column)),
});
data;
});
});
});
23 changes: 21 additions & 2 deletions packages/csv-parse/test/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,9 @@ describe("API Types", function () {

const typedOptions: Options<Person> = {};
typedOptions.columns = ["age", undefined, null, false, { name: "name" }];
typedOptions.columns = (record: Person) => {
record;
typedOptions.columns = (record) => {
const header: string[] = record;
header;
return ["age"];
};

Expand Down Expand Up @@ -501,6 +502,24 @@ describe("API Types", function () {
);
});

it("Accepts typed keys from a columns callback", function (next) {
const columnMapping = new Map<string, keyof Person>([
["full_name", "name"],
["years", "age"],
]);
parse<Person>(
"full_name,years\nAda,36",
{
columns: (header) =>
header.map((column) => columnMapping.get(column)),
},
(error, records: Person[]) => {
records;
next(error);
},
);
});

it("Exposes U[] and T if columns and on_record are specified", function (next) {
type PersonOriginal = { surname: string; age: number };
parse<Person, PersonOriginal>(
Expand Down