๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Etc

[ TS ] TypeScript 5.4 ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์•Œ์•„๋ณด๊ธฐ.

 

2024๋…„ 1์›” 24์ผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ 5.4 ๋ฒ ํƒ€ ๋ฒ„์ „ ๋ฐœํ‘œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

์ด ํฌ์ŠคํŒ…์€ ํ•ด๋‹น ๋ธ”๋กœ๊ทธ๋ฅผ ๋ณด๊ณ  ๊ณต๋ถ€ํ•˜๊ณ ์ž ํŒŒํŒŒ๊ณ ์™€ ํ•จ๊ป˜ ๋ฒˆ์—ญํ•˜๋ฉฐ ์˜ฎ๊ธด ๊ธ€์ด๋‹ค. (โ€ป์˜ค์—ญ ์ฃผ์˜)

 

 

 

 

 

์šฐ์„  TypeScript 5.4 beta ๋ฒ„์ „์€ ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋‹ค์šด๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

npm install -D typescript@beta

 

 


 

 

TypeScript 5.4์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๊ฐ„๋žตํ•˜๊ฒŒ ์†Œ๊ฐœํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

1. Preserved Narrowing in Clousures Following Last Assignments

   ๋งˆ์ง€๋ง‰ ํ• ๋‹น ํ›„ ํด๋กœ์ ธ์—์„œ ๋ฒ”์œ„๊ฐ€ ์ข์•„์ง€๋„๋ก ํƒ€์ž…์„ ์œ ์ง€(๋ณด์กด)ํ•˜๋‹ค.

2. NoInfer Utility Type

   NoInfer ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…
3. Object.groupBy and Map.groupBy

    Object.gruopBy ์™€ Map.groupBy

4. Support for require() calls in --moduleResolution bundler and --module preserve

    --moduleResolution bundler ์™€ --module preserve์—์„œ require() ํ˜ธ์ถœ ์ง€์›.

5. Checked Import Attributes and Assertions

    Import ์˜ attributes ๋ฐ assertions ํ™•์ธ

6. Quick Fix for Adding Missing Parameters

    ๋ˆ„๋ฝ๋œ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ ๋น ๋ฅธ ์ˆ˜์ •.

7. Upcoming 5.5 Deprecations

    ํ–ฅํ›„ 5.5 ๋ฒ„์ „์„ ์œ„ํ•œ ์ค€๋น„.

8. Breaking Changes

    ๋ณ€๊ฒฝ์  ๋ฟŒ์‹œ๊ธฐ

 

 

 

 

์ด์ค‘์— ๊ฐ€์žฅ ์ธ์ƒ๊นŠ์—ˆ๋˜ 1,2๋ฒˆ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๊ณ  ํ•œ๋‹ค.


 

 

Preserved Narrowing in Closures Following Last Assignments

TypeScript๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒ€์‚ฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณ€์ˆ˜์— ๋Œ€ํ•œ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์ธ ์œ ํ˜•์„ ์•Œ์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ณผ์ •์„ narrowing์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

function uppercaseStrings(x: string | number) {
    if (typeof x === "string") {
        // TypeScript knows 'x' is a 'string' here.
        return x.toUpperCase();
    }
}

 

์œ„์™€๊ฐ™์€ ์˜ˆ์‹œ์˜ ํ•œ ๊ฐ€์ง€ ๊ณตํ†ต์ ์ธ ๋ฌธ์ œ์ ์€ ์ด๋Ÿฐ ์ข์•„์ง„ ์œ ํ˜•์ด ํ•จ์ˆ˜์˜ ํด๋กœ์ ธ ๋‚ด์—์„œ ํ•ญ์ƒ ์œ ์ง€๋˜์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

 

function getUrls(url: string | URL, names: string[]) {
    if (typeof url === "string") {
        url = new URL(url);
    }

    return names.map(name => {
        url.searchParams.set("name", name)
        //  ~~~~~~~~~~~~
        // error!
        // Property 'searchParams' does not exist on type 'string | URL'.

        return url.toString();
    });
}

์—ฌ๊ธฐ์„œ TypeScript๋Š” url์ด ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ณ€ํ˜•๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ๋กœ ์šฐ๋ฆฌ์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—์„œ URL ๊ฐ์ฒด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๋Š” ๊ฒƒ์ด "์•ˆ์ „"ํ•˜์ง€ ์•Š๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ฒฝ์šฐ์—๋Š” ํ•ญ์ƒ url์— ๋Œ€ํ•œ ํ• ๋‹น ํ›„์— ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜๋ฉฐ url์— ๋Œ€ํ•œ ๋งˆ์ง€๋ง‰ ํ• ๋‹น์ด๊ธฐ๋„ ํ•œ๋‹ค.

 


TypeScript 5.4๋Š” ์ด ์ด์ ์„ ํ™œ์šฉํ•˜์—ฌ ๋ฒ”์œ„๋ฅผ ์กฐ๊ธˆ ๋” ํ˜„๋ช…ํ•˜๊ฒŒ ์ขํžŒ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜์™€ let ๋ณ€์ˆ˜๊ฐ€ ๋น„์Šน๊ฐ• ํ•จ์ˆ˜์— ์‚ฌ์šฉ๋˜๋ฉด, ํƒ€์ž… ์ฒด์ปค๋Š” ๋งˆ์ง€๋ง‰ ํ• ๋‹น ์ง€์ ์„ ์ฐพ์„ ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ ๋ฐœ๊ฒฌ๋˜๋ฉด, TypeScript๋Š” ํฌํ•จ ํ•จ์ˆ˜์˜ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฒ”์œ„๋ฅผ ์ขํž ์ˆ˜ ์žˆ๋‹ค.

 


์ค‘์ฒฉ๋œ ํ•จ์ˆ˜์˜ ์–ด๋Š ๊ณณ์—๋‚˜ ๋ณ€์ˆ˜๊ฐ€ ํ• ๋‹น๋˜์–ด ์žˆ์œผ๋ฉด ์ข์€ ํ•ด์„์ด ์‹œ์ž‘๋˜์ง€ ์•Š๋Š”๋‹ค. ํ•จ์ˆ˜๊ฐ€ ๋‚˜์ค‘์— ํ˜ธ์ถœ๋ ์ง€ ํ™•์‹คํžˆ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

function printValueLater(value: string | undefined) {
    if (value === undefined) {
        value = "missing!";
    }

    setTimeout(() => {
        // Modifying 'value', even in a way that shouldn't affect
        // its type, will invalidate type refinements in closures.
        value = value;
    }, 500);

    setTimeout(() => {
        console.log(value.toUpperCase());
        //          ~~~~~
        // error! 'value' is possibly 'undefined'.
    }, 1000);
}

์ด๊ฒƒ์€ ๋งŽ์€ ์ „ํ˜•์ ์ธ JavaScript ์ฝ”๋“œ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ๊ฒƒ์ด๋‹ค. ๊นƒํ—ˆ๋ธŒ์—์„œ ๊ทธ ๋ณ€ํ™”์— ๋Œ€ํ•ด ๋” ๋งŽ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

 

 

 

 

 

 

 

The NoInfer Utility Type

์ผ๋ฐ˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, TypeScript๋Š” ๋‹น์‹ ์ด ์ „๋‹ฌํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์—์„œ ํƒ€์ž… ์ธ์ˆ˜๋ฅผ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

function doSomething<T>(arg: T) {
    // ...
}


// We can explicitly say that 'T' should be 'string'.
doSomething<string>("hello!");

// We can also just let the type of 'T' get inferred.
doSomething("hello!");

๊ทธ๋Ÿฌ๋‚˜ ํ•œ ๊ฐ€์ง€ ๋ฌธ์ œ๋Š” ์ถ”๋ก ํ•˜๋Š” "์ตœ์ƒ์˜" ํƒ€์ž…์ด ๋ฌด์—‡์ธ์ง€ ํ•ญ์ƒ ๋ช…ํ™•ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋กœ ์ธํ•ด TypeScript๊ฐ€ ์œ ํšจํ•œ ํ˜ธ์ถœ์„ ๊ฑฐ๋ถ€ํ•˜๊ฑฐ๋‚˜, ์˜์‹ฌ์Šค๋Ÿฌ์šด ํ˜ธ์ถœ์„ ์ˆ˜๋ฝํ•˜๊ฑฐ๋‚˜, ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์„ ๋•Œ ๋” ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๊ณ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด, defaultColor(์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜)์™€ ํ•จ๊ป˜ colors(์ƒ‰์ƒ ์ด๋ฆ„)๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ค๋Š” CreateStreetLight ํ•จ์ˆ˜๋ฅผ ์ƒ์ƒํ•ด ๋ณด์ž.

function createStreetLight<C extends string>(colors: C[], defaultColor?: C) {
    // ...
}

createStreetLight(["red", "yellow", "green"], "red");

์›๋ž˜ colors ๋ฐฐ์—ด(C[])์— ์—†๋Š” defaultColor๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ์ด ๊ธฐ๋Šฅ์—์„œ ์ƒ‰์ƒ์€ "์ง„์‹ค์˜ ๊ทผ์›(source of truth)"์ด ๋˜์–ด์•ผ ํ•˜๊ณ  defaultColor๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด์šฉ์„ ์„ค๋ช…ํ•œ๋‹ค.

 

// Oops! This undesirable, but is allowed!
createStreetLight(["red", "yellow", "green"], "blue");

์ด ํ˜ธ์ถœ์—์„œ ํƒ€์ž… ์ธํ„ฐํŽ˜์ด์Šค๋Š” "ํŒŒ๋ž€์ƒ‰(blue)"์ด "๋นจ๊ฐ„์ƒ‰(red)" ๋˜๋Š” "๋…ธ๋ž€์ƒ‰(yellow)" ๋˜๋Š” "๋…น์ƒ‰(green)"๊ณผ ๊ฐ™์ด ํƒ€์ž…์˜ ํƒ€๋‹น์„ฑ์ด ์žˆ๋‹ค๊ณ  ๊ฒฐ์ •ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ TypeScript๋Š” ํ˜ธ์ถœ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ๋Œ€์‹ , C[] ํƒ€์ž…์„ "๋นจ๊ฐ„์ƒ‰" | "๋…ธ๋ž€์ƒ‰" | "๋…น์ƒ‰" | "ํŒŒ๋ž€์ƒ‰"์œผ๋กœ ์ถ”๋ก ํ•œ๋‹ค.

 


์‚ฌ๋žŒ๋“ค์ด ํ˜„์žฌ ์ด ๋ฌธ์ œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๊ธฐ์กด ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜์— ์˜ํ•ด ๊ฒฝ๊ณ„์ง€์–ด์ง€๋Š” ๋ณ„๋„์˜ ์œ ํ˜• ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

function createStreetLight<C extends string, D extends C>(colors: C[], defaultColor?: D) {
}

createStreetLight(["red", "yellow", "green"], "blue");
//                                            ~~~~~~
// error!
// Argument of type '"blue"' is not assignable to parameter of type '"red" | "yellow" | "green" | undefined'.

์ด๊ฒƒ์€ ํšจ๊ณผ๊ฐ€ ์žˆ์ง€๋งŒ, createStreetLight์˜ ๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” D๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„ ์–ด์ƒ‰ํ•˜๋‹ค. 

 


์ด๊ฒƒ์ด TypeScript 5.4๊ฐ€ ์ƒˆ๋กœ์šด NoInfer ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ๋„์ž…ํ•˜๋Š” ์ด์œ ์ด๋‹ค. NoInfer<...>์˜ ํƒ€์ž…์„ ๋‘˜๋Ÿฌ์‹ธ๋ฉด TypeScript์— ํƒ€์ž… ์ถ”๋ก ์„ ์œ„ํ•œ ํ›„๋ณด๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€ ํƒ€์ž…๋“ค์„ ํŒŒ๊ณ ๋“ค์–ด ๋Œ€์กฐํ•˜์ง€ ๋ง๋ผ๋Š” ์‹ ํ˜ธ๋ฅผ ์ค€๋‹ค.

 


NoInfer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CreateStreetLight๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹ค์‹œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค:

function createStreetLight<C extends string>(colors: C[], defaultColor?: NoInfer<C>) {
    // ...
}

createStreetLight(["red", "yellow", "green"], "blue");
//                                            ~~~~~~
// error!
// Argument of type '"blue"' is not assignable to parameter of type '"red" | "yellow" | "green" | undefined'.

defaultColor์„ ํƒ€์ž… ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•˜๋Š” ๊ฒƒ์€ "ํŒŒ๋ž€์ƒ‰"์ด ๊ฒฐ์ฝ” ์ธํ„ฐํŽ˜์ด์Šค ํ›„๋ณด๋กœ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ, ์œ ํ˜• ๊ฒ€์‚ฌ์ž๋Š” ์ด๋ฅผ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

 


 

๐ŸŒธ ์ถœ์ฒ˜ ๐ŸŒธ

 

MS - typescript dev blog