logo hsb.horse
← Retour au blog

Blog

Une fonction utilitaire TypeScript pour convertir des valeurs en ReadableStream

Une fonction utilitaire qui convertit des chaînes, objets et autres valeurs en ReadableStream. Une façon de manipuler des flux typés sans passer par Blob.

Publié:

Quand on veut convertir des valeurs primitives comme des chaînes, ou même des objets, en ReadableStream, une méthode courante consiste à passer d’abord par Blob.

Le problème, c’est qu’un flux généré via Blob devient un flux de Uint8Array. Ce n’est pas forcément une erreur, et pour des opérations binaires c’est probablement la voie la plus efficace.

Malgré cela, je voulais une petite fonction utilitaire capable de traiter directement des chaînes et d’autres types de valeurs, alors je l’ai écrite.

Code

type StreamResource<T> = T extends Blob
? Uint8Array
: T extends Uint8Array | ArrayBuffer
? Uint8Array
: T extends undefined | null | symbol
? never
: T;
function toReadableStream<T>(value: T): ReadableStream<StreamResource<T>> {
if (value === undefined || value === null || typeof value === "symbol") {
throw new TypeError(
"Cannot convert undefined, null, or symbol to ReadableStream"
);
}
if (value instanceof Blob) {
return value.stream() as ReadableStream<StreamResource<T>>;
}
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
const uint8Array = new Uint8Array(value);
return new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(uint8Array);
controller.close();
},
}) as ReadableStream<StreamResource<T>>;
}
return new ReadableStream<StreamResource<T>>({
start(controller) {
controller.enqueue(value as StreamResource<T>);
controller.close();
},
});
}

Exemples d’utilisation

const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
const blobStream = toReadableStream(blob);
// ^? ReadableStream<Uint8Array>
const buffer = new ArrayBuffer(8);
const bufferStream = toReadableStream(buffer);
// ^? ReadableStream<Uint8Array>
const uint8Array = new Uint8Array([1, 2, 3, 4]);
const uint8ArrayStream = toReadableStream(uint8Array);
// ^? ReadableStream<Uint8Array>
const str = 'Hello, TypeScript!';
const strStream = toReadableStream(str);
// ^? ReadableStream<string>
const num = 42;
const numStream = toReadableStream(num);
// ^? ReadableStream<number>
const undefStream = toReadableStream(undefined);
// ^? ReadableStream<never>

Points clés

  • Conversion typée avec les Conditional Types
  • Blob et ArrayBuffer deviennent des flux de Uint8Array
  • Les string, number et autres types similaires restent tels quels
  • undefined, null et symbol sont traités comme des erreurs de type

Résumé

En envoyant directement les valeurs dans un ReadableStream, on obtient une création de flux plus souple qu’en passant systématiquement par Blob.

L’inférence de types fonctionne bien aussi, donc cela devient une utilité pratique en TypeScript.