refactor: move buildRows to CsvImportEvent method with typed return#154
refactor: move buildRows to CsvImportEvent method with typed return#154
buildRows to CsvImportEvent method with typed return#154Conversation
commit: |
|
/review |
| validRows: totalRows - warnings.length, | ||
| correctedRows, | ||
| warningRows: warnings.length, |
There was a problem hiding this comment.
[1/1 — Medium] warningRows counts issues, not distinct rows — contradicts JSDoc
warnings.length is the count of CsvCellIssue objects, not distinct rows. If row 0 has two warning-level issues, warningRows = 2 and validRows = totalRows - 2, even though only one row has warnings. This contradicts the JSDoc added in types.ts:
/** Rows that have at least one warning-level issue. */
warningRows: number;Note that correctedRows correctly uses new Set() to count distinct rows — warningRows should do the same.
The existing test suite only places warnings on different rows, which masks this inconsistency.
Suggested fix:
const warningRows = new Set(warnings.map((w) => w.rowIndex)).size;
return {
totalRows,
validRows: totalRows - warningRows,
correctedRows,
warningRows,
skippedRows: 0,
};And add a corresponding test case:
it("counts distinct warning rows (multiple issues on the same row count as one)", () => {
const issues: CsvCellIssue[] = [
{ rowIndex: 0, columnKey: "name", level: "warning", message: "too short" },
{ rowIndex: 0, columnKey: "price", level: "warning", message: "unusual" },
];
expect(buildSummary(3, issues, [])).toEqual({
totalRows: 3,
validRows: 2, // 1 distinct warning row, not 2
correctedRows: 0,
skippedRows: 0,
warningRows: 1, // 1 row with warnings, not 2 issues
});
});Verdict: Request Changes
One Medium issue — the JSDoc-implementation mismatch on warningRows/validRows semantics is a new inconsistency introduced by this PR (the JSDoc is new; the pre-existing code had no such documentation claim). Everything else looks well-structured: the const T extends CsvSchema inference, InferCsvRow(T), readonly narrowings, and removal of the redundant buildRows standalone export are all solid improvements.
Summary
Improve the
buildRowsinterface by moving it to a method onCsvImportEvent, with a fully typed return value inferred from the schema definition. Also extractbuildSummaryas a testable function and add unit tests for summary statistics.Before
After
Motivation
buildRowsreturnedRecord<string, unknown>[], losing all type information that is derivable from the schema definition. Since AppShell is designed to work with Tailor Platform's GraphQL backend, integration with type-safe tools liketyped-document-noderequires that imported rows carry proper types — returningRecord<string, unknown>[]forces users to manually cast or assert types, which defeats the purpose of end-to-end type safety.useCsvImporteralready receives theschema, yetbuildRowsrequired it again as a second argument. By making it a method on the event object, the schema is captured in a closure and the redundant parameter is eliminated.Changes
CsvImportEvent<T>generic with abuildRows()methodInferCsvRow<T>utility type — infers row shape from schema column keys andStandardSchemaV1output typesconst T extends CsvSchemainuseCsvImporterto preserve literal types withoutas constCsvSchema.columns/CsvColumn.aliasesreadonly(backward-compatible, required forconst T)buildRowsfrom public API (replaced byevent.buildRows())InferCsvRowtype from public APIbuildSummaryinto a testable function with unit tests for summary statistics (warnings, corrections, combined scenarios)CsvImportEvent.summaryfields