logo hsb.horse
← 블로그 목록으로 돌아가기

블로그

값을 ReadableStream으로 변환하는 TypeScript 유틸리티 함수

String이나 객체 등을 ReadableStream으로 변환하는 유틸리티 함수. Blob을 거치지 않고 임의 타입으로 스트림을 다루는 구현을 정리했다.

게시일:

String 같은 원시 값이나 객체를 ReadableStream 으로 바꿀 때는 보통 한 번 Blob 으로 변환하는 방법을 많이 쓴다.

다만 Blob 을 거쳐 스트림을 만들면 Uint8Array 로 바뀌어 버린다. 바이너리 연산이 가장 효율적일 수 있으니 그것 자체가 틀렸다고 생각하는 건 아니다.

그래도 String 같은 값도 그대로 다룰 수 있는 유틸리티 함수가 있으면 좋겠다고 생각해서 적어 봤다.

코드

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();
},
});
}

사용 예시

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>

포인트

  • Conditional Types로 타입 안전하게 변환
  • Blob / ArrayBufferUint8Array 스트림으로 변환
  • String / number 등은 원래 타입 그대로 스트림화
  • undefined / null / symbol 은 타입 에러 처리

정리

값을 직접 ReadableStream 에 enqueue 하면 Blob 을 거치는 방식보다 유연하게 스트림을 만들 수 있다.

타입 추론도 잘 되기 때문에 TypeScript에서 쓰기 편한 유틸리티가 된다.