|
| 1 | +/** |
| 2 | + * Type-level regression tests for Editor#exportDocx overload resolution. |
| 3 | + * |
| 4 | + * Runtime: vitest sees one trivial assertion and passes. |
| 5 | + * Compile-time: every annotated assignment inside `_typeOnlyAssertions` fails |
| 6 | + * `tsc --noEmit` if an overload ever stops resolving to the expected return |
| 7 | + * type. The function is never called — it exists solely to force the type |
| 8 | + * checker to evaluate the assignments. |
| 9 | + * |
| 10 | + * File uses `.spec.ts` (not `.test.ts`) so tsconfig.json still type-checks it |
| 11 | + * while tsconfig.build.json keeps it out of the published dist. |
| 12 | + */ |
| 13 | + |
| 14 | +import { describe, it, expect } from 'vitest'; |
| 15 | +import type { Editor } from './Editor.js'; |
| 16 | + |
| 17 | +// Never invoked — pure type-level assertions. Wrapped in a function so vitest |
| 18 | +// doesn't try to execute the assignments at module load time. |
| 19 | + |
| 20 | +function _typeOnlyAssertions(editor: Editor): void { |
| 21 | + // Three narrow overloads. |
| 22 | + const _xmlOnly: Promise<string> = editor.exportDocx({ exportXmlOnly: true }); |
| 23 | + const _jsonOnly: Promise<string> = editor.exportDocx({ exportJsonOnly: true }); |
| 24 | + const _updatedDocs: Promise<Record<string, string | null>> = editor.exportDocx({ getUpdatedDocs: true }); |
| 25 | + |
| 26 | + // Default overload: T defaults to Blob, so browser consumers get |
| 27 | + // Promise<Blob> without casting. |
| 28 | + const _defaultNoArgs: Promise<Blob> = editor.exportDocx(); |
| 29 | + const _defaultWithParams: Promise<Blob> = editor.exportDocx({ commentsType: 'external' }); |
| 30 | + |
| 31 | + // Bare call, no contextual type — the default `T = Blob` must fire here, or |
| 32 | + // consumers still get the `Blob | Buffer` union downstream. |
| 33 | + const _bareInferred = editor.exportDocx(); |
| 34 | + const _proveBareIsBlob: Promise<Blob> = _bareInferred; |
| 35 | + |
| 36 | + // Node-headless consumers opt in to Buffer. |
| 37 | + const _explicitBuffer: Promise<Buffer> = editor.exportDocx<Buffer>(); |
| 38 | + const _explicitBlob: Promise<Blob> = editor.exportDocx<Blob>(); |
| 39 | + |
| 40 | + // Soundness guard: combining an explicit type argument with a narrow-flag |
| 41 | + // param must NOT compile. Without this guard, `<Buffer>({ getUpdatedDocs: true })` |
| 42 | + // typed as `Promise<Buffer>` while the runtime returned a file map. |
| 43 | + // @ts-expect-error getUpdatedDocs: true is incompatible with the default overload |
| 44 | + editor.exportDocx<Buffer>({ getUpdatedDocs: true }); |
| 45 | + // @ts-expect-error exportXmlOnly: true is incompatible with the default overload |
| 46 | + editor.exportDocx<Buffer>({ exportXmlOnly: true }); |
| 47 | + // @ts-expect-error exportJsonOnly: true is incompatible with the default overload |
| 48 | + editor.exportDocx<Buffer>({ exportJsonOnly: true }); |
| 49 | + |
| 50 | + // Customer scenario: Angular's `fromPromise` expects `Promise<T>` and yields |
| 51 | + // `Observable<T>`. Mirroring its shape — the default overload must flow into |
| 52 | + // `Promise<Blob>` without cast or type argument. (slack: p1776255665152579) |
| 53 | + const fromPromise = <T>(_p: Promise<T>): { value: T } => ({ value: undefined as unknown as T }); |
| 54 | + const _angularBlob: { value: Blob } = fromPromise(editor.exportDocx({ commentsType: 'external' })); |
| 55 | + |
| 56 | + // Silence unused-variable warnings. |
| 57 | + void _xmlOnly; |
| 58 | + void _jsonOnly; |
| 59 | + void _updatedDocs; |
| 60 | + void _defaultNoArgs; |
| 61 | + void _defaultWithParams; |
| 62 | + void _proveBareIsBlob; |
| 63 | + void _explicitBuffer; |
| 64 | + void _explicitBlob; |
| 65 | + void _angularBlob; |
| 66 | +} |
| 67 | + |
| 68 | +describe('Editor#exportDocx overload resolution (type-only)', () => { |
| 69 | + it('passes when the file type-checks', () => { |
| 70 | + expect(true).toBe(true); |
| 71 | + }); |
| 72 | +}); |
0 commit comments