Improve types for read[File], write[File], sheet_to_json

This commit is contained in:
lionel-rowe 2023-11-27 17:44:27 +08:00
parent 9199c2600c
commit 291e4eee53
2 changed files with 182 additions and 16 deletions

67
types/index.d.ts vendored

@ -19,21 +19,50 @@ export function set_fs(fs: any): void;
/** Set internal codepage tables */
export function set_cptable(cptable: any): void;
/** NODE ONLY! Attempts to read filename and parse */
export function readFile(filename: string, opts?: ParsingOptions): WorkBook;
/** Attempts to parse data */
export function read(data: any, opts?: ParsingOptions): WorkBook;
/**
* Attempts to synchronously read and parse a file from its supplied file path.
*
* NOTE: This function is only supported in server-side environments that
* provide synchronous file-reading APIs (e.g. NodeJS)
*/
export function readFile(filePath: string, opts?: ParsingOptions): WorkBook;
/**
* Attempts to parse the data supplied
*
* @param data the data to parse, in one of the following formats:
*
* - `"buffer"` (`Uint8Array` or NodeJS `Buffer`): binary data consisting of
* 8-bit unsigned integers
* - `"array"` (`ArrayBuffer`)
* - `"base64"` (`string`): Base64 encoding of the file
* - `"file"` (`string`): path of file that will be read (server-side
* environments only. See also {@link readFile}.)
* - `"string"` (`string`): JS string (only appropriate for UTF-8 text formats)
* - `"binary"` (`string`): binary string (byte `n` is `data.charCodeAt(n)`)
*
* Some common formats are automatically deduced from the data input type,
* including `Uint8Array` and `ArrayBuffer` objects and NodeJS `Buffer`
* objects. Alternatively, the format can be explicitly supplied as
* `opts.type`.
*
* When a string is passed with no `type`, the library assumes the data is a
* Base64 string. `FileReader#readAsBinaryString` or ASCII data requires
* "binary" type. DOM strings, including `FileReader#readAsText`, should use
* type "string".
*/
export function read(data: ArrayBuffer | Uint8Array | string, opts?: ParsingOptions): WorkBook;
/** Attempts to write or download workbook data to file */
export function writeFile(data: WorkBook, filename: string, opts?: WritingOptions): any;
export function writeFile(data: WorkBook, filename: string, opts?: WritingOptions): void;
/** Attempts to write or download workbook data to XLSX file */
export function writeFileXLSX(data: WorkBook, filename: string, opts?: WritingOptions): any;
export function writeFileXLSX(data: WorkBook, filename: string, opts?: WritingOptions): void;
/** Attempts to write or download workbook data to file asynchronously */
type CBFunc = () => void;
export function writeFileAsync(filename: string, data: WorkBook, opts: WritingOptions | CBFunc, cb?: CBFunc): any;
export function writeFileAsync(filename: string, data: WorkBook, opts: WritingOptions | CBFunc, cb?: CBFunc): void;
/** Attempts to write the workbook data */
export function write(data: WorkBook, opts: WritingOptions): any;
export function write<T extends WritingOptionsType = WritingOptionsType>(data: WorkBook, opts: WritingOptions<T>):
T extends 'buffer' ? Uint8Array : T extends 'array' ? ArrayBuffer : string;
/** Attempts to write the workbook data as XLSX */
export function writeXLSX(data: WorkBook, opts: WritingOptions): any;
export function writeXLSX(data: WorkBook, opts: WritingOptions): void;
/** Utility Functions */
export const utils: XLSX$Utils;
@ -253,11 +282,12 @@ export interface ParsingOptions extends UTCOption, CommonOptions, DenseOption {
PRN?: boolean;
}
type WritingOptionsType = 'base64' | 'binary' | 'buffer' | 'file' | 'array' | 'string';
/** Options for write and writeFile */
export interface WritingOptions extends CommonOptions {
export interface WritingOptions<T extends WritingOptionsType = WritingOptionsType> extends CommonOptions {
/** Output data encoding */
type?: 'base64' | 'binary' | 'buffer' | 'file' | 'array' | 'string';
type: T;
/**
* Generate Shared String Table
@ -805,9 +835,11 @@ export interface Sheet2HTMLOpts {
footer?: string;
}
export interface Sheet2JSONOpts extends DateNFOption {
export type Sheet2JSONCellValue<DefaultVal = undefined> = string | number | boolean | DefaultVal;
export interface Sheet2JSONOpts<DefaultVal = undefined> extends DateNFOption {
/** Output format */
header?: "A"|number|string[];
header?: 'A' | 1 | readonly string[];
/** Override worksheet range */
range?: any;
@ -816,7 +848,7 @@ export interface Sheet2JSONOpts extends DateNFOption {
blankrows?: boolean;
/** Default value for null/undefined values */
defval?: any;
defval?: DefaultVal;
/** if true, return raw data; if false, return formatted text */
raw?: boolean;
@ -920,9 +952,12 @@ export interface XLSX$Utils {
/* --- Export Functions --- */
/** Converts a worksheet object to an array of JSON objects */
sheet_to_json<ColumnKey extends string, DefaultVal = undefined>(worksheet: WorkSheet, opts?: Sheet2JSONOpts<DefaultVal> & { header: readonly ColumnKey[] }): Record<ColumnKey, Sheet2JSONCellValue<DefaultVal>>[];
sheet_to_json<ColumnKey extends string, DefaultVal = undefined>(worksheet: WorkSheet, opts?: Sheet2JSONOpts<DefaultVal> & { header?: undefined }): Record<string, Sheet2JSONCellValue<DefaultVal>>[];
sheet_to_json<ColumnKey extends string, DefaultVal = undefined>(worksheet: WorkSheet, opts?: Sheet2JSONOpts<DefaultVal> & { header: 'A' }): Record<string, Sheet2JSONCellValue<DefaultVal>>[];
sheet_to_json<ColumnKey extends string, DefaultVal = undefined>(worksheet: WorkSheet, opts?: Sheet2JSONOpts<DefaultVal> & { header: 1 }): Sheet2JSONCellValue<DefaultVal>[][];
// ...or manually supply a row type
sheet_to_json<T>(worksheet: WorkSheet, opts?: Sheet2JSONOpts): T[];
sheet_to_json(worksheet: WorkSheet, opts?: Sheet2JSONOpts): any[][];
sheet_to_json(worksheet: WorkSheet, opts?: Sheet2JSONOpts): any[];
/** Generates delimiter-separated-values output */
sheet_to_csv(worksheet: WorkSheet, options?: Sheet2CSVOpts): string;

@ -74,3 +74,134 @@ const vbawb = XLSX.readFile("test.xlsm", {bookVBA:true});
if(vbawb.vbaraw) {
const cfb: any /* XLSX.CFB.CFB$Container */ = CFB.read(vbawb.vbaraw, {type: "buffer"});
}
// read
{
XLSX.read(new Uint8Array(0));
XLSX.read(new ArrayBuffer(0));
XLSX.read(new Uint8Array(0), { type: 'buffer' });
XLSX.read(new ArrayBuffer(0), { type: 'array' });
XLSX.read('', { type: 'base64' });
// throw at runtime as the path is empty, but type checking should succeed
assertThrows(() => XLSX.read('', { type: 'file' }));
XLSX.read('', { type: 'string' });
XLSX.read('', { type: 'binary' });
// @ts-expect-error plain object can't be parsed
XLSX.read({});
// @ts-expect-error array can't be parsed
XLSX.read([]);
}
// write
{
const wb = XLSX.read(new Uint8Array(0));
const u8 = XLSX.write(wb, { type: 'buffer' });
const arrayBuffer = XLSX.write(wb, { type: 'array' });
const b64 = XLSX.write(wb, { type: 'base64' });
assert(u8 instanceof Uint8Array);
assert(arrayBuffer instanceof ArrayBuffer);
assert(typeof b64 === 'string');
// @ts-expect-error this will fail both at compile time and runtime without `opts.type`
assertThrows(() => XLSX.write(wb));
// @ts-expect-error this will fail both at compile time and runtime without `opts.type`
assertThrows(() => XLSX.write(wb, {}));
}
// sheet_to_json (compile-time type checking only)
{
const wb = XLSX.read(new Uint8Array(0));
const ws = wb.Sheets[wb.SheetNames[0]];
const _1 = XLSX.utils.sheet_to_json(ws, { header: 1 });
_1[0] = [1, undefined, 'str', true];
const _a = XLSX.utils.sheet_to_json(ws, { header: ['a', 'b', 'c', 'd'] });
_a[0] = { a: 1, b: 2, c: 3, d: undefined };
// @ts-expect-error unrecognized key `e`
_a[0] = { a: 1, b: 2, c: 3, d: 4, e: 5 };
// @ts-expect-error missing `d`
_a[0] = { a: 1, b: 2, c: 3 };
const header2 = ['a', 'b', 'c', 'd'] as const;
const _a2 = XLSX.utils.sheet_to_json(ws, { header: header2 });
_a2[0] = { a: 1, b: 2, c: 3, d: undefined };
// @ts-expect-error unrecognized key `e`
_a2[0] = { a: 1, b: 2, c: 3, d: 4, e: 5 };
// @ts-expect-error missing `d`
_a2[0] = { a: 1, b: 2, c: 3 };
const header3 = ['a', 'b', 'c', 'd'];
const _a3 = XLSX.utils.sheet_to_json(ws, { header: header3 });
_a3[0] = { key: 'val' };
// @ts-expect-error array not object
_a3[0] = ['val'];
const _A = XLSX.utils.sheet_to_json(ws, { header: 'A' });
_A[0] = { key: 'val' };
// @ts-expect-error array not object
_A[0] = ['val'];
const _u = XLSX.utils.sheet_to_json(ws, { header: undefined });
_u[0] = { key: 'val' };
// @ts-expect-error array not object
_u[0] = ['val'];
const _u2 = XLSX.utils.sheet_to_json(ws);
_u2[0] = { key: 'val' };
// @ts-expect-error array not object
_u2[0] = ['val'];
const _u3 = XLSX.utils.sheet_to_json(ws, {});
_u3[0] = { key: 'val' };
// @ts-expect-error array not object
_u3[0] = ['val'];
// manually supplying a type acts as a type assertion
const _x = XLSX.utils.sheet_to_json<[string, number]>(ws);
_x[0] = ['str', 1];
// @ts-expect-error wrong column order
_x[0] = [1, 'str'];
const _defVal = XLSX.utils.sheet_to_json(ws, { defval: new Date(0) });
_defVal[0] = { key: new Date(0), key2: 'str' };
// @ts-ignore - this will only error when strict null checking enabled `undefined` not allowed
_defVal[0] = { key: undefined };
const _defVal2 = XLSX.utils.sheet_to_json(ws, { header: 1, defval: new Date(0) });
_defVal2[0] = [new Date(0), 'str'];
// @ts-ignore - this will only error when strict null checking enabled `undefined` not allowed
_defVal2[0] = [new Date(0), 'str', undefined];
const _defVal3 = XLSX.utils.sheet_to_json(ws, { header: ['a', 'b'], defval: new Date(0) });
_defVal3[0] = { a: new Date(0), b: 'str' };
// @ts-expect-error unrecognized key `c`
_defVal3[0] = { a: new Date(0), b: 2, c: new Date(0) };
// @ts-expect-error missing `b`
_defVal3[0] = { a: 1 };
// @ts-expect-error `undefined` not allowed
_defVal3[0] = { a: undefined };
}
function assert(condition: boolean, message?: string) {
if (!condition) {
throw new Error(message ?? 'condition returned false');
}
}
function assertThrows(fn: (() => any), message?: string) {
let thrown = false;
try {
fn();
} catch {
thrown = true;
}
if (!thrown) throw new Error(message ?? 'function failed to throw');
}