Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve type of WritableStreamDefaultController.signal #157

Merged
merged 7 commits into from
Jan 5, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* The `next()` and `return()` methods of `ReadableStream`'s async iterator are now correctly "chained",
such that the promises returned by *either* of these methods are always resolved in the same order
as those methods were called.
* 💅 Improve type of `WritableStreamDefaultController.signal`. ([#157](https://github.com/MattiasBuelens/web-streams-polyfill/pull/157))

## 4.0.0 (2024-02-28)

Expand Down
12 changes: 8 additions & 4 deletions etc/web-streams-polyfill.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
```ts

// @public
export interface AbortSignal {
readonly aborted: boolean;
addEventListener(type: 'abort', listener: () => void): void;
export type AbortSignal = typeof globalThis extends {
AbortSignal: {
prototype: infer T;
};
} ? T : {
aborted: boolean;
readonly reason?: any;
addEventListener(type: 'abort', listener: () => void): void;
removeEventListener(type: 'abort', listener: () => void): void;
}
};

// @public
export class ByteLengthQueuingStrategy implements QueuingStrategy<ArrayBufferView> {
Expand Down
6 changes: 6 additions & 0 deletions src/globals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/// <reference lib="dom" />

declare global {
// From @types/node
// eslint-disable-next-line no-var
var global: typeof globalThis;
}

function getGlobals(): typeof globalThis | undefined {
if (typeof globalThis !== 'undefined') {
return globalThis;
Expand Down
46 changes: 11 additions & 35 deletions src/lib/abort-signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,16 @@
* via its associated `AbortController` object.
*
* @remarks
* This interface is compatible with the `AbortSignal` interface defined in TypeScript's DOM types.
* It is redefined here, so it can be polyfilled without a DOM, for example with
* {@link https://www.npmjs.com/package/abortcontroller-polyfill | abortcontroller-polyfill} in a Node environment.
* This is equivalent to the `AbortSignal` interface defined in TypeScript's DOM types or `@types/node`.
*
* @public
*/
export interface AbortSignal {
/**
* Whether the request is aborted.
*/
readonly aborted: boolean;

/**
* If aborted, returns the reason for aborting.
*/
export type AbortSignal = typeof globalThis extends { AbortSignal: { prototype: infer T } } ? T : {
aborted: boolean;
readonly reason?: any;

/**
* Add an event listener to be triggered when this signal becomes aborted.
*/
addEventListener(type: 'abort', listener: () => void): void;

/**
* Remove an event listener that was previously added with {@link AbortSignal.addEventListener}.
*/
removeEventListener(type: 'abort', listener: () => void): void;
}
};

export function isAbortSignal(value: unknown): value is AbortSignal {
if (typeof value !== 'object' || value === null) {
Expand All @@ -47,32 +30,25 @@ export function isAbortSignal(value: unknown): value is AbortSignal {
* A controller object that allows you to abort an `AbortSignal` when desired.
*
* @remarks
* This interface is compatible with the `AbortController` interface defined in TypeScript's DOM types.
* It is redefined here, so it can be polyfilled without a DOM, for example with
* {@link https://www.npmjs.com/package/abortcontroller-polyfill | abortcontroller-polyfill} in a Node environment.
* This is equivalent to the `AbortController` interface defined in TypeScript's DOM types or `@types/node`.
*
* @internal
*/
export interface AbortController {
// Trick with globalThis inspired by @types/node
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0c370ead967cb97b1758d8fa15d09011fb3f58ea/types/node/globals.d.ts#L226
export type AbortController = typeof globalThis extends { AbortController: { prototype: infer T } } ? T : {
readonly signal: AbortSignal;

abort(reason?: any): void;
}

interface AbortControllerConstructor {
new(): AbortController;
}

const supportsAbortController = typeof (AbortController as any) === 'function';
};

/**
* Construct a new AbortController, if supported by the platform.
*
* @internal
*/
export function createAbortController(): AbortController | undefined {
if (supportsAbortController) {
return new (AbortController as AbortControllerConstructor)();
if (typeof AbortController === 'function') {
return new AbortController();
}
return undefined;
}
2 changes: 0 additions & 2 deletions src/lib/readable-stream/async-iterator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2018.asynciterable" />

import { ReadableStream } from '../readable-stream';
import {
AcquireReadableStreamDefaultReader,
Expand Down
9 changes: 8 additions & 1 deletion src/stub/dom-exception.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/// <reference types="node" />
import { globals } from '../globals';
import { setFunctionName } from '../lib/helpers/miscellaneous';

declare global {
interface ErrorConstructor {
// From @types/node
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
}
}

interface DOMException extends Error {
name: string;
message: string;
Expand Down
2 changes: 0 additions & 2 deletions src/stub/math-trunc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2015.core" />

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc#Polyfill
const MathTrunc: typeof Math.trunc = Math.trunc || function (v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
Expand Down
2 changes: 0 additions & 2 deletions src/stub/number-isfinite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2015.core" />

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite#Polyfill
const NumberIsFinite: typeof Number.isFinite = Number.isFinite || function (x) {
return typeof x === 'number' && isFinite(x);
Expand Down
2 changes: 0 additions & 2 deletions src/stub/number-isinteger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2015.core" />

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill
const NumberIsInteger: typeof Number.isInteger = Number.isInteger || function (value) {
return typeof value === 'number'
Expand Down
2 changes: 0 additions & 2 deletions src/stub/number-isnan.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2015.core" />

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#Polyfill
const NumberIsNaN: typeof Number.isNaN = Number.isNaN || function (x) {
// eslint-disable-next-line no-self-compare
Expand Down
2 changes: 0 additions & 2 deletions src/stub/symbol.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/// <reference lib="es2015.symbol" />

const SymbolPolyfill: (description?: string) => symbol
= typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
? Symbol
Expand Down
13 changes: 9 additions & 4 deletions test/types/writable-stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@ const closePromise: Promise<void> = writableStream.close();
const abortPromise: Promise<void> = writableStream.abort('aborted');

// Compatibility with stream types from DOM
// FIXME Remove deprecated WritableStreamDefaultController.abortReason
// FIXME Align our AbortSignal definition with TypeScript's version
// const domUnderlyingSink: UnderlyingSink<string> = underlyingSink;
declare global {
interface WritableStreamDefaultController {
// FIXME Remove deprecated WritableStreamDefaultController.abortReason
abortReason: any;
}
}

const domUnderlyingSink: UnderlyingSink<string> = underlyingSink;
const domWritableStream: WritableStream<string> = writableStream;
// const domController: WritableStreamDefaultController = controller;
const domController: WritableStreamDefaultController = controller;
const domWriter: WritableStreamDefaultWriter<string> = writer;
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"moduleResolution": "node",
"lib": [
"es5",
"es2015.core",
"es2015.promise",
"es2015.symbol",
"es2015.symbol.wellknown",
"es2018.asynciterable"
],
"newLine": "lf",
Expand Down
Loading