1. satisfies
잠깐! 4.9에 나온 satisfies는 사용하고 계신가요?
객체 타입을 검사할 때 생각보다 typescript가 똑똑하게 검사하지 않아서 4.9에 새로 등장했습니다. Typescript에서 설명하기 위한 예시로 palette라는 객체를 예로 들었습니다.
// RGB tuple이나 string을 허용하고 있습니다.
const palette = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255] // blue에 오타가 있습니다.
};
// 배열의 첫 번째 값을 가져오고 싶지만 string일수도 있어서 허용되지 않습니다.
const redComponent = palette.red.at(0);
// 배열일 수 있어서 이 또한 허용되지 않습니다.
const greenNormalized = palette.green.toUpperCase();
타입은 정의했지만 사용할 떄 문제가 있습니다. 만약 Record로 아래와 같이 타입을 지정한다면 이제 Colors union 타입으로 'blue'의 오타를 찾을 수는 있지만 여전히 redComponent, greenNormalized를 사용할 순 없습니다.
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette: Record<Colors, string | RGB> = ...
이 때 satisfies 키워드를 사용한다면 원하는 오타를 찾고 red와 blue는 tuple타입으로 그리고 green은 string으로 지정할 수 있습니다.
const palette = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255] // 에러 발견 가능!
} satisfies Record<Colors, string | RGB>;
// 사용 가능!
const redComponent = palette.red.at(0);
const greenNormalized = palette.green.toUpperCase();
2. using
본론인 Typescript 5.2에서 새로운 키워드인 'using' 를 살펴보겠습니다.
'using' 을 사용하면 Symbol.dispose 함수를 통해 스코프를 벗어날 때 모든 것을 폐기할 수 있습니다.
{
const getResource = () => {
return {
[Symbol.dispose]: () => {
console.log('Hooray!')
}
}
}
using resource = getResource();
} // 'Hooray!' logged to console
자바스크립트 TC39 Proposal 4단계 중 최근 3단계 해당됩니다. 파일 처리, 데이터베이스 연결과 같은 리소스 관리에 아주 효율적입니다.
TC39 Proposal은 ECMAScript 표준에 추가될 새로운 기능들을 제안하는 문서입니다. (Stage0-Stage4)
Symbol.dispose
새롭게 추가된 자바스크립트 전역 심볼로 Symbol.dispose 함수로 정의된 모든 것들이 특정 수명을 갖는 '리소스'로 간주되며 using 키워드와 함께 사용할 수 있습니다.
const resource = {
[Symbol.dispose]: () => {
console.log("Hooray!");
},
};
await using
비동기적으로 리소스를 폐기하기 위해선 Symbol.asyndDispose 와 await using을 사용할 수 있습니다.
const getResource = () => ({
[Symbol.asyncDispose]: async () => {
await someAsyncFunc();
},
});
{
await using resource = getResource();
}
database connection과 같이 프로그램을 진행하기 전에 연결이 닫혔는지 검증해야할 때 유용합니다.
Use cases
파일 처리
node에서 file handler를 통한 파일 시스템에 접근할 때 using을 사용하면 엄청 쉬워집니다. 만약 using 없이 아래와 같이 구현된 코드를
import { open } from "node:fs/promises";
let filehandle;
try {
filehandle = await open("thefile.txt", "r");
} finally {
await filehandle?.close();
}
using을 사용하면 다음과 같이 구현할 수 있습니다.
import { open } from "node:fs/promises";
const getFileHandle = async (path: string) => {
const filehandle = await open(path, "r");
return {
filehandle,
[Symbol.asyncDispose]: async () => {
await filehandle.close();
},
};
};
{
await using file = await getFileHandle("thefile.txt");
// Do stuff with file.filehandle
} // Automatically disposed!
database connection
c#에서도 가장 많이 보여지는 사례입니다. using 없이 사용한다면:
const connection = await getDb();
try {
// Do stuff with connection
} finally {
await connection.close();
}
using을 사용한다면:
const getConnection = async () => {
const connection = await getDb();
return {
connection,
[Symbol.asyncDispose]: async () => {
await connection.close();
},
};
};
{
await using db = await getConnection();
// Do stuff with db.connection
} // Automatically closed!
stream, file reader 객체의 리소스를 release하거나 읽거나 하는 동작들은 에러 발생시 전체 프로그램을 멈출 수 있기 때문에 항상try .. catch 구문 내부에서 사용해야 합니다. finally를 사용한다 하더라도 finally 이후에 handle객체가 참조되는 것을 방지할 수 없습니다. 선언된 코드가 handle이 초기화된 곳과 같은 스코프에 있기 때문입니다. 한 개의 리소스가 아닌 여러개의 리소스에서 try .. catch .. finally 또한 문제가 됩니다. 앞으로 using이 명시를 훨씬 쉽게 도와줄 수 있습니다.
https://www.totaltypescript.com/typescript-5-2-new-keyword-using
www.totaltypescript.com](https://www.totaltypescript.com/typescript-5-2-new-keyword-using)
https://velog.io/@jay/Typescript-5.2-using(https://velog.io/@jay/Typescript-5.2-using)
'software engineering > javascript' 카테고리의 다른 글
[Knip] Find unused files, dependencies and exports in JavaScript and TypeScript projects (0) | 2024.02.03 |
---|---|
JavaScript 개발자를 위한 객체 속성 삭제 전략: undefined vs delete (0) | 2024.01.01 |
forEach is BAD! (0) | 2023.10.03 |
Package Manager 뭐 써볼까? (0) | 2023.09.21 |
void 언제 쓸까? (0) | 2023.08.27 |