Typescript: ์ œ์•ˆ: ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•

์— ๋งŒ๋“  2016๋…„ 01์›” 22์ผ  ยท  146์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: microsoft/TypeScript

์†์„ฑ์ด ๋ฌธ์ž์—ด(๋˜๋Š” ๋ฌธ์ž์—ด ์ง‘ํ•ฉ)์ผ ์ˆ˜๋Š” ์—†์ง€๋งŒ ํŒจํ„ด๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

let fontStyle: 'normal' | 'italic' = 'normal'; // already available in master
let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i = '#000'; // my suggestion

JavaScript์—์„œ๋Š” DOM ๋…ธ๋“œ ๋˜๋Š” ๋‹ค์–‘ํ•œ ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ CSS ์Šคํƒ€์ผ ๋ฐ˜์˜๊ณผ ๊ฐ™์ด CSS ํ‘œ๊ธฐ๋ฒ•์œผ๋กœ ์ƒ‰์ƒ ๊ฐ’์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜๋‚˜์š”?

Literal Types Needs Proposal Suggestion

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋””์ž์ธ ์ œ์•ˆ

๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฌธ์ž์—ด๋ณด๋‹ค ๋” ๋งŽ์€ ์ง€์ •๋œ ๊ฐ’์ด ํ•„์š”ํ•˜์ง€๋งŒ CSS ์ƒ‰์ƒ, ์ด๋ฉ”์ผ, ์ „ํ™” ๋ฒˆํ˜ธ, ZipCode, swagger ํ™•์žฅ ๋“ฑ๊ณผ ๊ฐ™์€ ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ์กฐํ•ฉ์œผ๋กœ ์—ด๊ฑฐํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ json ์Šคํ‚ค๋งˆ ์‚ฌ์–‘๋„ JSON ๊ฐœ์ฒด์˜ ์Šคํ‚ค๋งˆ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด ๋ฐ patternProperties ๋Š” TS ์œ ํ˜• ์‹œ์Šคํ…œ ์ธก๋ฉด์—์„œ regex-validated string type ๋ฐ regex-validated string type of index ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชฉํ‘œ

๊ฐœ๋ฐœ์ž๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” JSON ์Šคํ‚ค๋งˆ์— ํ•œ ๊ฑธ์Œ ๋” ๊ฐ€๊นŒ์šด ์œ ํ˜• ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ ๋ฌธ์ž์—ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์žŠ์–ด๋ฒ„๋ฆฌ์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ ๊ฐœ์š”

์ด ๊ธฐ๋Šฅ์˜ ๊ตฌํ˜„์€ 4๊ฐ€์ง€ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

์ •๊ทœ์‹ ๊ฒ€์ฆ ์œ ํ˜•

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;

์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ณ€์ˆ˜ ์œ ํ˜•

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;

๋™์ผํ•˜์ง€๋งŒ ๋” ์ฝ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

let fontColor: CssColor;

์ธ๋ฑ์Šค์˜ ์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ณ€์ˆ˜ ์œ ํ˜•

interface UsersCollection {
    [email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i]: User;
}

๋™์ผํ•˜์ง€๋งŒ ๋” ์ฝ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

interface UsersCollection {
    [email: Email]: User;
}

๊ฐ€๋ณ€ํ˜• ํƒ€์ž… ๊ฐ€๋“œ

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) {
        fontColor = color;// correct
    }
}

๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color))) return;
    fontColor = color;// correct
}

๋” ๋‚˜์€ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ์ •์˜๋œ ์œ ํ˜• ์‚ฌ์šฉ

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
        fontColor = color;// correct
    }
}

๊ฐ™์€

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(CssColor.test(color))) return;
    fontColor = color;// correct
}

์ธ๋ฑ์Šค ์œ ํ˜•์— ๋Œ€ํ•œ ์œ ํ˜• ๊ฐ€๋“œ

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email)) {
        collection[email];// type is User
    }
}

๊ฐ™์€

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email))) return;
    collection[email];// type is User
}

๋” ๋‚˜์€ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ์ •์˜๋œ ์œ ํ˜• ์‚ฌ์šฉ

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
    }
}

๊ฐ™์€

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(Email.test(email))) return;
    collection[email];// type is User
}

์‹œ๋งจํ‹ฑ ๊ฐœ์š”

๊ณผ์ œ

let email: Email;
let gmail: Gmail;
email = '[email protected]';// correct
email = '[email protected]';// correct
gmail = '[email protected]';// compile time error
gmail = '[email protected]';// correct
gmail = email;// obviously compile time error
email = gmail;// unfortunately compile time error too

๋ถˆํ–‰ํžˆ๋„ ์šฐ๋ฆฌ๋Š” ์ด ๋ฌธ์„œ ๋กœ ์ธํ•ด ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์˜ํ–ฅ ์—†์ด ํ•œ ์ •๊ทœ์‹์ด ๋‹ค๋ฅธ ์ •๊ทœ์‹์˜ ํ•˜์œ„ ์œ ํ˜•์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ œํ•œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

// explicit cast
gmail = <Gmail>email;// correct
// type guard
if (Gmail.test(email)) {
    gmail = email;// correct
}
// another regex subtype declaration
type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
gmail = email;// correct

๋ถˆํ–‰ํžˆ๋„ string ๋ณ€์ˆ˜๋ฅผ regex-validated ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๋Š” ๊ฒƒ๋„ ์ œํ•œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์ •๊ทœ์‹๊ณผ ์ผ์น˜ํ•œ๋‹ค๋Š” ๋ณด์žฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

let someEmail = '[email protected]';
let someGmail = '[email protected]';
email = someEmail;// compile time error
gmail = someGmail;// compile time error

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋œ ๋Œ€๋กœ ๋ช…์‹œ์  ์บ์ŠคํŠธ ๋˜๋Š” ์œ ํ˜• ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜
์šด ์ข‹๊ฒŒ๋„ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ๊ฒฝ์šฐ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ ํ•ด๋‹น ๊ฐ’์ด ์ •๊ทœ์‹๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
email = someEmail;// correct
gmail = someGmail;// correct

์ธ๋ฑ์Šค์˜ ์œ ํ˜• ์ถ•์†Œ

regex-validated type ์ธ๋ฑ์Šค์˜ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๋Š” ์ธ๋ฑ์Šค ์œ ํ˜•์— ๋Œ€ํ•œ ์œ ํ˜• ๋ณดํ˜ธ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
๊ทธ๋Ÿฌ๋‚˜ ๋” ๋ณต์žกํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
interface UsersCollection {
    [email: Email]: User;
    [gmail: Gmail]: GmailUser;
}
let collection: UsersCollection;
let someEmail = '[email protected]';
let someGmail = '[email protected]';
collection['[email protected]'];// type is User
collection['[email protected]'];// type is User & GmailUser
collection[someEmail];// unfortunately type is any
collection[someGmail];// unfortunately type is any
// explicit cast is still an unsafe workaround
collection[<Email> someEmail];// type is User
collection[<Gmail> someGmail];// type is GmailUser
collection[<Email & Gmail> someGmail];// type is User & GmailUser

๋ฆฌํ„ฐ๋Ÿด์—๋Š” ๊ทธ๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

let collection: UsersCollection;
let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
collection[someEmail];// type is User
collection[someGmail];// type is User & GmailUser

๊ทธ๋Ÿฌ๋‚˜ ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ ๊ฐ€์žฅ ์ข‹์€ ์˜ต์…˜์€ ๋‹ค์Œ๋ณด๋‹ค ํ˜„์‹ค์ ์ธ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์œ ํ˜• ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is GmailUser
    }
}

๊ทธ๋Ÿฌ๋‚˜ Gmail ์œ ํ˜•์— ๋Œ€ํ•ด ๋” ๋‚˜์€ ์ •์˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ์œ ํ˜•์ด ์ถ•์†Œ๋ฉ๋‹ˆ๋‹ค.

type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is User & GmailUser
    }
}

์กฐํ•ฉ ๋ฐ ๊ต์ฐจ๋กœ

์‹ค์ œ๋กœ ์ผ๋ฐ˜์ ์ธ ์œ ํ˜•๊ณผ regex-validated ์œ ํ˜•์€ ์‹ค์ œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ•ฉ์ง‘ํ•ฉ๊ณผ ๊ต์ฐจ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ทœ์น™์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;// correct
type test_2 = Regex_1 & Regex_2;// correct
type test_3 = Regex_1 | NonRegex;// correct
type test_4 = Regex_1 & NonRegex;// compile time error
if (test_1.test(something)) {
    something;// type is test_1
    // something matches Regex_1 OR Regex_2
}
if (test_2.test(something)) {
    something;// type is test_2
    // something matches Regex_1 AND Regex_2
}
if (test_3.test(something)) {
    something;// type is Regex_1
} else {
    something;// type is NonRegex
}

์ œ๋„ค๋ฆญ

์ œ๋„ค๋ฆญ์—๋Š” ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์—†์œผ๋ฏ€๋กœ regex-validated type์€ ์ผ๋ฐ˜ ํƒ€์ž…๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ œ๋„ค๋ฆญ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์€ ์ œ์•ฝ ์กฐ๊ฑด์ด ์žˆ๋Š” ์ œ๋„ค๋ฆญ์˜ ๊ฒฝ์šฐ regex-validated ์œ ํ˜•์€ ๋ฌธ์ž์—ด์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

class Something<T extends String> { ... }
let something = new Something<Email>();// correct

๊ฐœ์š” ๋ฐฉ์ถœ

์ผ๋ฐ˜์ ์ธ ์œ ํ˜•๊ณผ ๋‹ฌ๋ฆฌ regex-validated ๋Š” ๋ฐฉ์ถœ์— ์•ฝ๊ฐ„์˜ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;
type test_2 = Regex_1 & Regex_2;
type test_3 = Regex_1 | NonRegex;
type test_4 = Regex_1 & NonRegex;
if (test_1.test(something)) {
    /* ... */
}
if (test_2.test(something)) {
    /* ... */
}
if (test_3.test(something)) {
    /* ... */
} else {
    /* ... */
}

๋‹ค์Œ์œผ๋กœ ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค.

var Regex_1 = / ... /;
var Regex_2 = / ... /;
if (Regex_1.test(something) || Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something) && Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something)) {
    /* ... */
} else {
    /* ... */
}

ํ˜ธํ™˜์„ฑ ๊ฐœ์š”

์ด ๊ธฐ๋Šฅ์€ ํ˜ธํ™˜์„ฑ์— ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๊ฒƒ์„ ๊นจ๋œจ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ ์žˆ๊ณ  regex-validated ์œ ํ˜•์ด ์ผ๋ฐ˜ ์œ ํ˜•๊ณผ ๋‹ฌ๋ฆฌ ์˜ํ–ฅ์„ ๋ฐฉ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ฏ€๋กœ ์œ ํšจํ•œ TS ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

type someType = { ... };
var someType = { ... };

์•„๋ž˜ ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ:

type someRegex = / ... /;
var someRegex = { ... };

๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๋ฒˆ์งธ๋Š” ์ด๋ฏธ ์œ ํšจํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹ค๋ฅธ ์ด์œ ๋กœ ์ธํ•ด(์œ ํ˜• ์„ ์–ธ์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค).
๋”ฐ๋ผ์„œ ์ด์ œ ์ด ์œ ํ˜•์ด regex-validated ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ ์œ ํ˜•๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋ณ€์ˆ˜ ์„ ์–ธ์„ ์ œํ•œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ถ”์‹ 

์ œ๊ฐ€ ๋†“์ณค์„ ์ˆ˜๋„ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ž์œ ๋กญ๊ฒŒ ์ง€์ ํ•ด ์ฃผ์„ธ์š”. ์ด ์ œ์•ˆ์ด ๋งˆ์Œ์— ๋“ค๋ฉด ์ด๋ฅผ ๋‹ค๋ฃจ๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  PR๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  146 ๋Œ“๊ธ€

์˜ˆ, ๋‚˜๋Š” ์ด๊ฒƒ์„ ํ™•์‹คํžˆ Typed๋ฅผ ํ†ตํ•ด ๋น—์งˆํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๊ณ„์ธต์—์„œ ์ด์™€ ๊ฐ™์€ ๊ฒƒ์„

์ฃผ์š” ๋ฌธ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์ด๊ฒƒ๋“ค์„ ์ž˜ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ช…ํ™•ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "cat" , "dog" ๋ฐ "fish" ์˜ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ๋ชฉ๋ก์„ ์›ํ•˜๋ฉด /dog|cat|fish(,(dog|cat|fish))*/ ์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    • "cat" , "dog " ๋ฐ "fish" ๋Œ€ํ•œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ์„ค๋ช…ํ•˜๋Š” ์œ ํ˜•์ด ์ด๋ฏธ ์žˆ๋Š” ๊ฒฝ์šฐ ์ด ์ •๊ทœ์‹์— ์–ด๋–ป๊ฒŒ ํ†ตํ•ฉํ•ฉ๋‹ˆ๊นŒ?

    • ๋ถ„๋ช…ํžˆ ์—ฌ๊ธฐ์—๋Š” ๋ฐ”๋žŒ์งํ•˜์ง€ ์•Š์€ ๋ฐ˜๋ณต์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ด์ „ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์ด ์ž‘์—…์ด ๋” ์‰ฌ์›Œ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋น„ํ‘œ์ค€ ํ™•์žฅ์€ ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ iffy๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ์—„์ฒญ๋‚œ +1, ZipCode, SSN, ONet ๋ฐ ์ด์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ๋งŽ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€.

๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ๊ณ  ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ ์•„๋งˆ๋„ ์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
http://stackoverflow.com/questions/37144672/guid-uuid-type-in-typescript

@mhegazy๊ฐ€ ์ œ์•ˆํ•œ ๋Œ€๋กœ ์—ฌ๊ธฐ์— ๋‚ด ์ œ์•ˆ(#8665)์„ ๋„ฃ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜•์‹ ์„ ์–ธ์—์„œ ๊ฐ„๋‹จํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ทธ๋Ÿฐ ๊ฒƒ :

type Integer(n:number) => String(n).macth(/^[0-9]+$/)
let x:Integer = 3 //OK
let y:Integer = 3.6 //wrong

type ColorLevel(n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

type Hex(n:string) => n.match(/^([0-9]|[A-F])+$/)
let hexValue:Hex = "F6A5" //OK
let wrongHexValue:Hex = "F6AZ5" //wrong

์œ ํ˜•์ด ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์€ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์œ ํ˜•๊ณผ ํ•จ์ˆ˜ ํ‰๊ฐ€ ์ž์ฒด์— ์˜ํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด #7982๋„ ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

@rylphs +1 ์ด๊ฒƒ์€ TypeScript๋ฅผ ๋งค์šฐ ๊ฐ•๋ ฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

ํ•˜์œ„ ์œ ํ˜•์€ _regex๋กœ ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•_๊ณผ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ?

let a: RegExType_1
let b: RegExType_2

a = b // Is this allowed? Is RegExType_2 subtype of RegExType_1?
b = a // Is this allowed? Is RegExType_1 subtype of RegExType_2?

์—ฌ๊ธฐ์„œ RegExType_1 ๋ฐ RegExType_2 ๋Š” _regex๋กœ ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•_์ž…๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์ด ๋ฌธ์ œ๋Š” ๋‹คํ•ญ์‹ ์‹œ๊ฐ„์— ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค( ์ •๊ทœ ํ‘œํ˜„์‹์— ๋Œ€ํ•œ ํฌํ•จ ๋ฌธ์ œ ์ฐธ์กฐ).

TypeStyle์—๋„ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค: https://github.com/typestyle/typestyle/issues/5 :rose:

JSX์—์„œ @RyanCavanaugh ์™€ ์ €๋Š” ์‚ฌ๋žŒ๋“ค์ด aria- (๋ฐ ์ž ์žฌ์ ์œผ๋กœ data- ) ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์™„์ „ ํ˜•์‹์œผ๋กœ ConfirmTyped์— ๋ฌธ์ž์—ด ์ธ๋ฑ์Šค ์„œ๋ช…์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ƒˆ๋กœ์šด ์ƒ‰์ธ ์„œ๋ช…์ด ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

๋””์ž์ธ ์ œ์•ˆ

๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฌธ์ž์—ด๋ณด๋‹ค ๋” ๋งŽ์€ ์ง€์ •๋œ ๊ฐ’์ด ํ•„์š”ํ•˜์ง€๋งŒ CSS ์ƒ‰์ƒ, ์ด๋ฉ”์ผ, ์ „ํ™” ๋ฒˆํ˜ธ, ZipCode, swagger ํ™•์žฅ ๋“ฑ๊ณผ ๊ฐ™์€ ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ์กฐํ•ฉ์œผ๋กœ ์—ด๊ฑฐํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ json ์Šคํ‚ค๋งˆ ์‚ฌ์–‘๋„ JSON ๊ฐœ์ฒด์˜ ์Šคํ‚ค๋งˆ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด ๋ฐ patternProperties ๋Š” TS ์œ ํ˜• ์‹œ์Šคํ…œ ์ธก๋ฉด์—์„œ regex-validated string type ๋ฐ regex-validated string type of index ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชฉํ‘œ

๊ฐœ๋ฐœ์ž๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” JSON ์Šคํ‚ค๋งˆ์— ํ•œ ๊ฑธ์Œ ๋” ๊ฐ€๊นŒ์šด ์œ ํ˜• ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ ๋ฌธ์ž์—ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์žŠ์–ด๋ฒ„๋ฆฌ์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ ๊ฐœ์š”

์ด ๊ธฐ๋Šฅ์˜ ๊ตฌํ˜„์€ 4๊ฐ€์ง€ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

์ •๊ทœ์‹ ๊ฒ€์ฆ ์œ ํ˜•

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;

์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ณ€์ˆ˜ ์œ ํ˜•

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;

๋™์ผํ•˜์ง€๋งŒ ๋” ์ฝ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

let fontColor: CssColor;

์ธ๋ฑ์Šค์˜ ์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ณ€์ˆ˜ ์œ ํ˜•

interface UsersCollection {
    [email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i]: User;
}

๋™์ผํ•˜์ง€๋งŒ ๋” ์ฝ๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

interface UsersCollection {
    [email: Email]: User;
}

๊ฐ€๋ณ€ํ˜• ํƒ€์ž… ๊ฐ€๋“œ

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color)) {
        fontColor = color;// correct
    }
}

๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(color))) return;
    fontColor = color;// correct
}

๋” ๋‚˜์€ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ์ •์˜๋œ ์œ ํ˜• ์‚ฌ์šฉ

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
        fontColor = color;// correct
    }
}

๊ฐ™์€

setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (!(CssColor.test(color))) return;
    fontColor = color;// correct
}

์ธ๋ฑ์Šค ์œ ํ˜•์— ๋Œ€ํ•œ ์œ ํ˜• ๊ฐ€๋“œ

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email)) {
        collection[email];// type is User
    }
}

๊ฐ™์€

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(email))) return;
    collection[email];// type is User
}

๋” ๋‚˜์€ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ์ •์˜๋œ ์œ ํ˜• ์‚ฌ์šฉ

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
    }
}

๊ฐ™์€

let collection: UsersCollection;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (!(Email.test(email))) return;
    collection[email];// type is User
}

์‹œ๋งจํ‹ฑ ๊ฐœ์š”

๊ณผ์ œ

let email: Email;
let gmail: Gmail;
email = '[email protected]';// correct
email = '[email protected]';// correct
gmail = '[email protected]';// compile time error
gmail = '[email protected]';// correct
gmail = email;// obviously compile time error
email = gmail;// unfortunately compile time error too

๋ถˆํ–‰ํžˆ๋„ ์šฐ๋ฆฌ๋Š” ์ด ๋ฌธ์„œ ๋กœ ์ธํ•ด ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์˜ํ–ฅ ์—†์ด ํ•œ ์ •๊ทœ์‹์ด ๋‹ค๋ฅธ ์ •๊ทœ์‹์˜ ํ•˜์œ„ ์œ ํ˜•์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ œํ•œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

// explicit cast
gmail = <Gmail>email;// correct
// type guard
if (Gmail.test(email)) {
    gmail = email;// correct
}
// another regex subtype declaration
type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
gmail = email;// correct

๋ถˆํ–‰ํžˆ๋„ string ๋ณ€์ˆ˜๋ฅผ regex-validated ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๋Š” ๊ฒƒ๋„ ์ œํ•œ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์ •๊ทœ์‹๊ณผ ์ผ์น˜ํ•œ๋‹ค๋Š” ๋ณด์žฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

let someEmail = '[email protected]';
let someGmail = '[email protected]';
email = someEmail;// compile time error
gmail = someGmail;// compile time error

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋œ ๋Œ€๋กœ ๋ช…์‹œ์  ์บ์ŠคํŠธ ๋˜๋Š” ์œ ํ˜• ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜
์šด ์ข‹๊ฒŒ๋„ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ๊ฒฝ์šฐ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•˜๋Š” ๋™์•ˆ ํ•ด๋‹น ๊ฐ’์ด ์ •๊ทœ์‹๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
email = someEmail;// correct
gmail = someGmail;// correct

์ธ๋ฑ์Šค์˜ ์œ ํ˜• ์ถ•์†Œ

regex-validated type ์ธ๋ฑ์Šค์˜ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๋Š” ์ธ๋ฑ์Šค ์œ ํ˜•์— ๋Œ€ํ•œ ์œ ํ˜• ๋ณดํ˜ธ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
๊ทธ๋Ÿฌ๋‚˜ ๋” ๋ณต์žกํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type Email = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
type Gmail = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
interface UsersCollection {
    [email: Email]: User;
    [gmail: Gmail]: GmailUser;
}
let collection: UsersCollection;
let someEmail = '[email protected]';
let someGmail = '[email protected]';
collection['[email protected]'];// type is User
collection['[email protected]'];// type is User & GmailUser
collection[someEmail];// unfortunately type is any
collection[someGmail];// unfortunately type is any
// explicit cast is still an unsafe workaround
collection[<Email> someEmail];// type is User
collection[<Gmail> someGmail];// type is GmailUser
collection[<Email & Gmail> someGmail];// type is User & GmailUser

๋ฆฌํ„ฐ๋Ÿด์—๋Š” ๊ทธ๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

let collection: UsersCollection;
let someEmail: '[email protected]' = '[email protected]';
let someGmail: '[email protected]' = '[email protected]';
collection[someEmail];// type is User
collection[someGmail];// type is User & GmailUser

๊ทธ๋Ÿฌ๋‚˜ ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ ๊ฐ€์žฅ ์ข‹์€ ์˜ต์…˜์€ ๋‹ค์Œ๋ณด๋‹ค ํ˜„์‹ค์ ์ธ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์œ ํ˜• ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is GmailUser
    }
}

๊ทธ๋Ÿฌ๋‚˜ Gmail ์œ ํ˜•์— ๋Œ€ํ•ด ๋” ๋‚˜์€ ์ •์˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ์œ ํ˜•์ด ์ถ•์†Œ๋ฉ๋‹ˆ๋‹ค.

type Gmail = Email & /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@gmail\.com$/i;
getUserByEmail(email: string) {
    collection[email];// type is any
    if (Email.test(email)) {
        collection[email];// type is User
        if (Gmail.test(email)) {
            collection[email];// type is User & GmailUser
        }
    }
    if (Gmail.test(email)) {
        collection[email];// type is User & GmailUser
    }
}

์กฐํ•ฉ ๋ฐ ๊ต์ฐจ๋กœ

์‹ค์ œ๋กœ ์ผ๋ฐ˜์ ์ธ ์œ ํ˜•๊ณผ regex-validated ์œ ํ˜•์€ ์‹ค์ œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ•ฉ์ง‘ํ•ฉ๊ณผ ๊ต์ฐจ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ทœ์น™์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;// correct
type test_2 = Regex_1 & Regex_2;// correct
type test_3 = Regex_1 | NonRegex;// correct
type test_4 = Regex_1 & NonRegex;// compile time error
if (test_1.test(something)) {
    something;// type is test_1
    // something matches Regex_1 OR Regex_2
}
if (test_2.test(something)) {
    something;// type is test_2
    // something matches Regex_1 AND Regex_2
}
if (test_3.test(something)) {
    something;// type is Regex_1
} else {
    something;// type is NonRegex
}

์ œ๋„ค๋ฆญ

์ œ๋„ค๋ฆญ์—๋Š” ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์—†์œผ๋ฏ€๋กœ regex-validated type์€ ์ผ๋ฐ˜ ํƒ€์ž…๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ œ๋„ค๋ฆญ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์€ ์ œ์•ฝ ์กฐ๊ฑด์ด ์žˆ๋Š” ์ œ๋„ค๋ฆญ์˜ ๊ฒฝ์šฐ regex-validated ์œ ํ˜•์€ ๋ฌธ์ž์—ด์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

class Something<T extends String> { ... }
let something = new Something<Email>();// correct

๊ฐœ์š” ๋ฐฉ์ถœ

์ผ๋ฐ˜์ ์ธ ์œ ํ˜•๊ณผ ๋‹ฌ๋ฆฌ regex-validated ๋Š” ๋ฐฉ์ถœ์— ์•ฝ๊ฐ„์˜ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_1 = Regex_1 | Regex_2;
type test_2 = Regex_1 & Regex_2;
type test_3 = Regex_1 | NonRegex;
type test_4 = Regex_1 & NonRegex;
if (test_1.test(something)) {
    /* ... */
}
if (test_2.test(something)) {
    /* ... */
}
if (test_3.test(something)) {
    /* ... */
} else {
    /* ... */
}

๋‹ค์Œ์œผ๋กœ ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค.

var Regex_1 = / ... /;
var Regex_2 = / ... /;
if (Regex_1.test(something) || Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something) && Regex_2.test(something)) {
    /* ... */
}
if (Regex_1.test(something)) {
    /* ... */
} else {
    /* ... */
}

ํ˜ธํ™˜์„ฑ ๊ฐœ์š”

์ด ๊ธฐ๋Šฅ์€ ํ˜ธํ™˜์„ฑ์— ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ทธ๊ฒƒ์„ ๊นจ๋œจ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๋งŒ ์žˆ๊ณ  regex-validated ์œ ํ˜•์ด ์ผ๋ฐ˜ ์œ ํ˜•๊ณผ ๋‹ฌ๋ฆฌ ์˜ํ–ฅ์„ ๋ฐฉ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ฏ€๋กœ ์œ ํšจํ•œ TS ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

type someType = { ... };
var someType = { ... };

์•„๋ž˜ ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ:

type someRegex = / ... /;
var someRegex = { ... };

๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๋ฒˆ์งธ๋Š” ์ด๋ฏธ ์œ ํšจํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹ค๋ฅธ ์ด์œ ๋กœ ์ธํ•ด(์œ ํ˜• ์„ ์–ธ์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค).
๋”ฐ๋ผ์„œ ์ด์ œ ์ด ์œ ํ˜•์ด regex-validated ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ ์œ ํ˜•๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋ณ€์ˆ˜ ์„ ์–ธ์„ ์ œํ•œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ถ”์‹ 

์ œ๊ฐ€ ๋†“์ณค์„ ์ˆ˜๋„ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ž์œ ๋กญ๊ฒŒ ์ง€์ ํ•ด ์ฃผ์„ธ์š”. ์ด ์ œ์•ˆ์ด ๋งˆ์Œ์— ๋“ค๋ฉด ์ด๋ฅผ ๋‹ค๋ฃจ๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  PR๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

regex-validated ์œ ํ˜•์˜ ๊ต์ฐจ ๋ฐ ํ•ฉ์ง‘ํ•ฉ์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์‚ฌ๋ก€๋ฅผ ์ง€์ ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์—ˆ์ง€๋งŒ ์ตœ์‹  ํ…Œ์ŠคํŠธ ์‚ฌ๋ก€์—์„œ ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์‚ฌ์†Œํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด Design proposal ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

@Igmat , ๋””์ž์ธ ์ œ์•ˆ์— ๋Œ€ํ•œ ์งˆ๋ฌธ: ๋ฐฉ์ถœ ๊ฐœ์š”์— ๋Œ€ํ•ด ์ž์„ธํžˆ

@alexanderbird ์˜ˆ, ๋‹ค๋ฅธ ์œ ํ˜•์€ ๋ฐฉ์ถœ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” regex-validated ๋„ ๊ทธ๋ ‡๊ฒŒ ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ œ์•ˆ์„œ๋ฅผ ๋งŒ๋“ค๊ณ  ์ œ์•ˆ๋œ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ฒซ ๋ฒˆ์งธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

let fontColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
fontColor = "#000";

์ด:

type CssColor: /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
let fontColor: CssColor;
fontColor = "#000";

"#000" ๋Š” ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ดœ์ฐฎ๊ณ  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋‚ด๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ๊ทธ๊ฒƒ์„ ์œ ์šฉํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด string ์—์„œ regex-validated ์œ ํ˜•์œผ๋กœ ์ขํžˆ๋Š” ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ €๋Š” ์ด์ „์˜ ๋‘ ๊ฐ€์ง€ ์„ค์ •์— ๋Œ€ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

let someString: string;
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(someString)) {
    fontColor = someString; // Ok
}
fontColor = someString; // compile time error

๋”ฐ๋ผ์„œ ์ •๊ทœ ํ‘œํ˜„์‹์ด ์ž˜ ์ฝํžˆ์ง€ ์•Š๊ณ  ๋ชจ๋“  ์œ„์น˜์—์„œ ๋ณต์‚ฌ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์‰ฝ๊ฒŒ ์‹ค์ˆ˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๋ฐฉ์ถœ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ณ  ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ์—๋Š” type ์ž‘๋™ ๋ฐฉ์‹์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์—ฌ์ „ํžˆ ๋‚˜์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ด ๋ฌผ๊ฑด์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

let someString: string;
let email: /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/I;
if (/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i.test(someString)) {
    email = someString; // Ok
}
email = someString; // compile time error

์•…๋ชฝ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์€ ๊ต์ฐจ์™€ ๊ฒฐํ•ฉ์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด์™€ ๊ฐ™์€ ์ผ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋ ค๋ฉด ์ œ์•ˆ์— ํ‘œ์‹œ๋œ ๋Œ€๋กœ type emission์„ ์•ฝ๊ฐ„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@DanielRosenwasser , ์ด ์ œ์•ˆ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๊ทธ๋ฆฌ๊ณ  ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์—ฌ๊ธฐ์— ์–ธ๊ธ‰๋œ ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•ด์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๊นŒ?
๋‚œ ์ •๋ง์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋„์›€๋ง์„ ์›ํ•˜์ง€๋งŒ ๊ทธ๊ฒƒ์€ ๋งŽ์€ ์‹œ๊ฐ„์„ ํ•„์š”๋กœํ•œ๋‹ค ( tsc ์ •๋ง ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณต์žกํ•˜๊ณ  ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๋‚ด๋ถ€ ์ž‘๋™ ๋ฐฉ์‹์˜ ์ดํ•ด์— ์ผํ•ด์•ผ) ๋‚˜๋Š” ์•Œ๊ณ ์žˆ๋‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ์ด ์ œ์•ˆ์€ ๊ตฌํ˜„ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋‹ค๋ฅธ ์–ธ์–ด ๋””์ž์ธ ๋น„์ „์ด๋‚˜ ๋‹ค๋ฅธ ์ด์œ ๋กœ ์ธํ•ด ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋œ ์ด ๊ธฐ๋Šฅ์„ ๊ฑฐ๋ถ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Hey @Igmat , ์ฒ˜์Œ์— ๋ฌผ์–ด

์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๋ฐฉ์ถœํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ์ง€ ์ดํ•ดํ•˜์ง€ ๋ชปํ•˜๋ฉฐ ์œ ํ˜•์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ฐฉ์ถœ๋„ ํ—ˆ์šฉ๋˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋น„๋ชฉํ‘œ๋ฅผ ํ™•์ธ

๋‚ด๊ฐ€ ์ œ๊ธฐํ–ˆ์–ด์•ผ ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” ์—ญ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ •๊ทœ์‹ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ๋‚ด ์ดํ•ด(๋ฐ ๊ฒฝํ—˜)๋Š” ์ •๊ทœ์‹์˜ ์—ญ์ฐธ์กฐ๊ฐ€ ์ž…๋ ฅ์— ์ง€์ˆ˜์ ์ธ ์‹œ๊ฐ„์— ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฝ”๋„ˆ์ผ€์ด์Šค์ธ๊ฐ€์š”? ์•„๋งˆ๋„, ํ•˜์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ํ”ผํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํŽธ์ง‘๊ธฐ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์œ„์น˜์—์„œ ์œ ํ˜• ๊ฒ€์‚ฌ์— ์ตœ์†Œํ•œ์˜ ์‹œ๊ฐ„์ด ์†Œ์š”๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ํŠนํžˆ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” TypeScript ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์—”์ง„์— ์˜์กดํ•˜๊ฑฐ๋‚˜ ์ด๋Ÿฌํ•œ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ง€์ • ์ •๊ทœ์‹ ์—”์ง„์„ ๋นŒ๋“œํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, TC39๋Š” . ๊ฐ€ ๊ฐœํ–‰๊ณผ ์ผ์น˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ƒˆ๋กœ์šด s ํ”Œ๋ž˜๊ทธ๋ฅผ ํฌํ•จํ•˜๋„๋ก ์ด๋™ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ESXXXX์™€ ์ด๋ฅผ ์ง€์›ํ•˜๋Š” ์ด์ „ ๋Ÿฐํƒ€์ž„ ๊ฐ„์— ๋ถˆ์ผ์น˜๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@igmat - @DanielRosenwasser ์˜ ๋ง์—

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ๊ทธ๊ฒƒ์„ ์œ ์šฉํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ฌธ์ž์—ด์—์„œ ์ •๊ทœ์‹ ๊ฒ€์ฆ ์œ ํ˜•์œผ๋กœ ์ขํžˆ๋Š” ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋™์  ๋ฌธ์ž์—ด์—์„œ ์ •๊ทœ์‹ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์œ ํ˜•์œผ๋กœ ๋ฒ”์œ„๋ฅผ ์ขํž ๊ฒฝ์šฐ์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งค์šฐ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. ์ด ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ์—๋„:

function foo(bar: number) {
    let baz: /prefix:\d+/ = 'prefix:' + number;
}

์œ ํ˜•์ด ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ˆซ์ž๊ฐ€ ์Œ์ˆ˜์ด๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๊ทธ๋ฆฌ๊ณ  ์ •๊ทœ์‹์ด ๋” ๋ณต์žกํ•ด์ง€๋ฉด ์ ์  ๋” ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ด๊ฒƒ์„ ์ •๋ง๋กœ ์›ํ–ˆ๋‹ค๋ฉด ์•„๋งˆ๋„ "type interpolation: type Baz = /prefix:{number}/ ...๋ฅผ ํ—ˆ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฑฐ๊ธฐ์— ๊ฐˆ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋Œ€์‹ , ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆ๋œ ์œ ํ˜•์— ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด๋งŒ ํ• ๋‹นํ•˜๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด ๋ชฉํ‘œ์— ์–ด๋Š ์ •๋„ ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์„ ๊ณ ๋ คํ•˜์„ธ์š”:

type Color = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
let foo: Color = '#000000';
let bar: Color = '#0000'; // Error - string literal '#0000' is not assignable to type 'Color'; '#0000' does not match /^#([0-9a-f]{3}|[0-9a-f]{6})$/i
let baz: Color = '#' + config.userColorChoice; // Error - type 'string' is not assignable to type 'regex-validated-string'

์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๋Œ€์•ˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹ญ๋‹ˆ๊นŒ?

@DanielRosenwasser , ๋‚˜๋Š” Design Goals๋ฅผ ์ฃผ์˜ ๊นŠ๊ฒŒ ์ฝ์—ˆ๊ณ  , ๋‚ด๊ฐ€ ๋‹น์‹ ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•œ๋‹ค๋ฉด ๋ฌธ์ œ๋Š” Non-goals#5 ์œ„๋ฐ˜์ž…๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ ์œ„๋ฐ˜์ด ์•„๋‹ˆ๋ผ ๊ตฌ๋ฌธ ๊ฐœ์„ ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด์ „์—๋Š” ๋‹ค์Œ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

const emailRegex = /.../;
/**
 * assign it only with values tested to emailRegex 
 */
let email: string;
let userInput: string;
// somehow get user input
if (emailRegex.test(userInput)) {
    email = userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

์ด ์ œ์•ˆ์„ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

type Email = /.../;
let email: Email;
let userInput: string;
// somehow get user input
if (Email.test(userInput)) {
    email = userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

๋ณด์‹œ๋‹ค์‹œํ”ผ ์ฝ”๋“œ๋Š” ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ •๊ทœ์‹์˜ ์ผ๋ฐ˜์ ์ธ ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๋ฒˆ์งธ ๊ฒฝ์šฐ๋Š” ํ›จ์”ฌ ๋” ํ‘œํ˜„์ ์ด๊ณ  ์ •๊ทœ์‹ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์˜๋ฏธํ•˜๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ธฐ ์ „์— ๋ฌธ์ž์—ด์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ์žŠ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ ์‹ค์ˆ˜ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
๋‘ ๋ฒˆ์งธ๋Š” ์ด๋Ÿฌํ•œ ์œ ํ˜• ์ถ•์†Œ ์—†์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ธ๋ฑ์Šค์—์„œ ์ •๊ทœ์‹ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ์ธ๋ฑ์Šค ํ•„๋“œ๋Š” ๋ฆฌํ„ฐ๋Ÿด๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„์— ํ™•์ธํ•  ์ˆ˜ ์—†๋Š” ์ผ๋ถ€ ๋ณ€์ˆ˜์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. .

@alexanderbird , ์ด ์ฝ”๋“œ๋ฅผ ์œ ํšจํ•˜๊ฒŒ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๋Ÿฐํƒ€์ž„๊ณผ ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ๋ชจ๋‘์— ์ˆจ๊ฒจ์ง„ ๊ฒ€์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

function foo(bar: number) {
    let baz: /prefix:\d+/ = 'prefix:' + number;
}

์ด ์ฝ”๋“œ๋Š” ๋‚ด ์ œ์•ˆ์œผ๋กœ ์ธํ•ด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€:

function foo(bar: number) {
    let baz: /prefix:\d+/ = ('prefix:' + number) as /prefix:\d+/;
}

์•„๋‹ˆ๋ฉด ์ด๊ฑฐ:

function foo(bar: number) {
    let baz: /prefix:\d+/;
    let possibleBaz: string = 'prefix:' + number;
    if (/prefix:\d+/.test(possibleBaz)) {
        baz = possibleBaz;
    }
}

์ •ํ™•ํ•˜๊ณ  ๋ฐฉ์ถœ๋œ ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด์ „ ๋Œ“๊ธ€์—์„œ ๋ณด์—ฌ๋“œ๋ฆฐ ๊ฒƒ์ฒ˜๋Ÿผ ๋ฆฌํ„ฐ๋Ÿด์€ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ๋„ ํ™•์‹คํžˆ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์ข…์ข… ์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด๋‚˜ ๋‹ค๋ฅธ ์†Œ์Šค์˜ ์ŠคํŒ…์œผ๋กœ ์ž‘์—…ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ์ถœ ์˜ํ–ฅ์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์œผ๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋‹ค์Œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ด ์œ ํ˜•์œผ๋กœ ์ž‘์—…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

export type Email = /.../;
export const Email = /.../;
let email: Email;
let userInput: string;
// somehow get user input
if (Email.test(userInput)) {
    email = <Email>userInput;
} else {
    console.log('User provided invalid email. Showing validation error');
    // Some code for validation error
}

๋˜๋Š” ๊ต์ฐจ๋กœ์˜ ๊ฒฝ์šฐ:

export type Email = /email-regex/;
export const Email = /email-regex/;
export type Gmail = Email & /gmail-regex/;
export const Gmail = {
    test: (input: string) => Email.test(input) && /gmail-regex/.test(input)
};
let gmail: Gmail;
let userInput: string;
// somehow get user input
if (Gmail.test(userInput)) {
    gmail = <Gmail>userInput;
} else {
    console.log('User provided invalid gmail. Showing validation error');
    // Some code for validation error
}

์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๋•Œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ฝ”๋“œ๋ฅผ ๋ณต์ œํ•˜๊ณ  ๋ช…์‹œ์  ์บ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์š”ํ•˜๋Š” ๊ฒƒ์€ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉ์ถœ ์˜ํ–ฅ์€ ์ •๋ง ๋งค์šฐ ์ž‘๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉ์ถœ ๋ณ€๊ฒฝ ์—†์ด ์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋™์•ˆ ์‚ฌ์šฉ์ž๋ฅผ ๋†€๋ผ๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ์ผ๋ถ€ ๊ธฐ๋Šฅ์„ ์ž˜๋ชป ์ดํ•ดํ•˜๊ฑฐ๋‚˜ ์ฐพ๊ธฐ ์–ด๋ ค์šด ๋ฒ„๊ทธ๋กœ ์ด์–ด์ง€์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ regex-validated ์œ ํ˜•์€ ๋ฒ”์œ„ ๋ณ€์ˆ˜์ด์ž ์ปดํŒŒ์ผ๋Ÿฌ ์œ ํ˜•์ด๋ผ๋Š” ๊ฐ„๋‹จํ•œ ์šฉ์–ด๋กœ ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

@DanielRosenwasser ๋ฐ @alexanderbird ์ข‹์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๊ฐ€ ํ•˜๋‚˜ ๋” ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ๋ฌธ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

const type Email = /email-regex/;

์ด ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋Š” ์ด๊ฒƒ์„ type ๋ฐ const ๋‘˜ ๋‹ค๋กœ ์›ํ•œ๋‹ค๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์‹ค์ œ ์œ ํ˜• ์‹œ์Šคํ…œ์€ ๊ทธ๋Ÿฌํ•œ ์ˆ˜์ •์ž์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ํ•œ ๋ฐฉ์ถœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ผํ•œ ๋ฐฉ์ถœ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋งŽ์€ ์‹ค์ˆ˜, ์บ์ŠคํŠธ ๋ฐ ์ฝ”๋“œ ๋ณต์ œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const Email = /email-regex/;

์ด๊ฒƒ์€ ์•„๋งˆ๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ œ์•ˆ์— ๋Œ€ํ•œ ๋‹จ์ˆœํ•œ ๊ฐœ์„ ๋ณด๋‹ค ํ›จ์”ฌ ๋” ํฐ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(์˜ˆ: Redux ๊ฐ€ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐ€์ ธ์˜จ ๊ฒƒ):

export type SOME_ACTION = 'SOME_ACTION';
export const SOME_ACTION = 'SOME_ACTION' as SOME_ACTION;

๋กœ ๋ณ€ํ™˜ ์ค‘

export const type SOME_ACTION = 'SOME_ACTION';

๋น„์Šทํ•œ ์ œ์•ˆ์„ ์ฐพ์œผ๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ์„ฑ๊ณตํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜ ์žˆ๊ณ  ๊ทธ๋Ÿฌํ•œ ์•„์ด๋””์–ด๊ฐ€ ๋งˆ์Œ์— ๋“ค๋ฉด ๋””์ž์ธ ์ œ์•ˆ ๋ฐ ํ…Œ์ŠคํŠธ๋ฅผ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@DanielRosenwasser , ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ์— ๋Œ€ํ•ด - ๋‚ด ์ œ์•ˆ์—์„œ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•ด์„œ๋งŒ ์ •๊ทœ์‹์„ ์‹คํ–‰ํ•˜๊ณ  ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ๊ฒƒ ๊ฐ™์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

let something: /some-regex-with-backreferences/ = `
long enough string to make regex.test significantly affect performance
`

์–ด์จŒ๋“  ์šฐ๋ฆฌ๋Š” ์‹ค์‹œ๊ฐ„ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ ์œ„ํ•ด ๋ฆฌํ„ฐ๋Ÿด์ด ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ์ง€์†๋˜์–ด์•ผ ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ผ๋ถ€ ํŽธ์ง‘๊ธฐ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์— ์ง๋ฉดํ•˜๋Š” ๋™์•ˆ ํ™•์ธํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฒฝ๊ณ ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ฒฝํ—˜์  ๋ฐฉ๋ฒ•์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๊ฐ€ ์ปดํŒŒ์ผํ•  ๋•Œ ํ™•์ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ. ๋˜๋Š” ๋‹ค๋ฅธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ธ ๋ฒˆ์งธ ์งˆ๋ฌธ์— ๋Œ€ํ•ด ๋ชจ๋“  ๊ฒƒ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ–ˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๊ตฌํ˜„์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ target from tsconfig ์— ๋”ฐ๋ผ ์ •๊ทœ์‹ ์—”์ง„์„ ์„ ํƒํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์กฐ์‚ฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

@DanielRosenwass ์–ด๋–ค ์ƒ๊ฐ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๐Ÿ˜„ ์ดˆ๊ธฐ ์ œ์•ˆ๊ณผ ๋งˆ์ง€๋ง‰ ์ œ์•ˆ์— ๋Œ€ํ•ด. ๋‘ ๋ฒˆ์งธ์— ๋Œ€ํ•ด ๋” ์ž์„ธํ•œ ๊ฐœ์š”๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@Igmat ๊ท€ํ•˜์˜ ์ œ์•ˆ์€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๋ฌธ์ž์—ด ์œ ํ˜•์—์„œ๋งŒ ์œ ์šฉํ•˜๋„๋ก ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. @rylphs ์ œ์•ˆ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์„ธ์š”? ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ๊ธฐ๋ณธ ์œ ํ˜•์— ๋Œ€ํ•ด ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

type ColorLevel = (n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ธฐ๋ณธํ˜•์„ ๋„˜์–ด ๊ธฐ๋ณธํ˜•์ด ์•„๋‹Œ ์œ ํ˜•์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด ๋„ˆ๋ฌด ๋งŽ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
ํ•œ ๊ฐ€์ง€ ์ ์€ @DanielRosenwasser ๊ฐ€ ๋‹ค์–‘ํ•œ ์ •๊ทœ์‹ ์—”์ง„ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์ œ๊ธฐํ•œ ๋ฌธ์ œ๊ฐ€

@zspitz ์œ ๋งํ•ด ๋ณด์ด์ง€๋งŒ ์ œ ์ƒ๊ฐ์—๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ์„ฑ๋Šฅ์— ๋„ˆ๋ฌด ๋งŽ์€ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ด ๊ทœ์น™์— ์˜ํ•ด ์ œํ•œ๋˜์ง€ ์•Š๊ณ  TS๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ผ๋ถ€ ๋ฆฌ์†Œ์Šค์— ์˜์กดํ•˜๋Š” ์ผ๋ถ€ ํ‘œํ˜„์‹์„ ๊ณ„์‚ฐํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ ์‹œ๊ฐ„์—.

@์ด๊ทธ๋งˆํŠธ

๊ธฐ๋Šฅ์ด ์–ด๋–ค ๊ทœ์น™์— ์˜ํ•ด ์ œํ•œ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—

์—ผ๋‘์— ๋‘” ๋ช‡ ๊ฐ€์ง€ ๊ตฌ์ฒด์ ์ธ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋งˆ๋„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ตฌ๋ฌธ์„ Typescript์˜ "์•ˆ์ „ํ•œ"/์ปดํŒŒ์ผ ์‹œ ์•Œ๋ ค์ง„ ํ•˜์œ„ ์ง‘ํ•ฉ์œผ๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ •์˜ ์œ ํ˜• ๊ฐ€๋“œ๊ฐ€ ์ƒˆ๋กœ์šด ์œ ํ˜•์„ ์ •์˜ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

// type guard that introduces new nominal type int
function isInt(value: number): value is type int { return /^\d+$/.test(value.toString()); }
// -------------------------------------^^^^ add type keyword here
function printNum(value: number) { console.log(value); }
function printInt(value: int) { console.log(value); }
const num = 123;
printNum(num); // ok
printInt(num); // error
if (isInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
}

@disjukr ์€ ์ข‹์•„ ๋ณด์ด์ง€๋งŒ ์œ ํ˜• ํ™•์žฅ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

ํ™•์žฅ ๊ฐ€๋Šฅํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์„ ์š”๊ตฌํ•˜๋Š” TypeScript ๋””์ž์ธ ์›์น™์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ํ™•์žฅ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๋”๋ผ๋„ @disjukr์ด ์ œ์•ˆํ•˜๋Š” ๋ช…๋ชฉ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š”

ํ™•์žฅ์„ฑ IMHO๋ฅผ ์–ป์œผ๋ ค๋ฉด ๊ฝค ์ฐฝ์˜์ ์ธ ๊ฒƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์ž„์˜ ์œ ํ˜• ๊ฐ€๋“œ(ํ•จ์ˆ˜)๊ฐ€ ๋‹ค๋ฅธ ์ž„์˜ ์œ ํ˜• ๊ฐ€๋“œ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์œ ํ˜• ์ฃผ์žฅ ์‚ฌ๊ณ  ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ์ ์ธ "ํ™•์žฅ์„ฑ"์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function isInt(value: number): value is type int { return /^\d+$/.test(value.toString()); }
// assert that biggerInt extends int. No compiler or runtime check that it actually does extend.
function isBiggerInt(value: number): value is type biggerInt extends int { return /^\d{6,}$/.test(value.toString()); }
// -----------------------------------------------------------^^^^ type extension assertion
function printNum(value: number) { console.log(value); }
function printInt(value: int) { console.log(value); }
function printBiggerInt(value: biggerInt) {console.log(value); }

const num = 123;
printNum(num); // ok
printInt(num); // error
printBiggerInt(num); // error
if (isInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
    printBiggerInt(num); // error
}
if (isBiggerInt(num)) {
    printNum(num); // ok
    printInt(num); // ok
    printBiggerInt(num); // ok
}

์†Œ๋ฆฌ๊ฐ€ ๋‚˜์ง€ ์•Š๋”๋ผ๋„ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฒ˜์Œ์— ๋งํ–ˆ๋“ฏ์ด ํ™•์žฅ ๊ฐ€๋Šฅํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ, ์•„๋‹ˆ๋ฉด @disjukr์ด ์ œ์•ˆํ•œ ๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? (ํ›„์ž์˜ ๊ฒฝ์šฐ @disjukr ์˜ ํ™•์žฅ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.)

์ฃผ์ œ ์—์„œ @DanielRosenwasser ์ฒซ ๋ฒˆ์งธ ๋Œ“๊ธ€์— ๋‹ต์žฅ:
์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ๋ชฉ๋ก์˜ ๊ฒฝ์šฐ ^ ๋ฐ $ ์•ต์ปค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์ผ๋ถ€ ๋ฌธ์ž์—ด์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋ ค๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๊ด€๋ จ๋จ). ๊ทธ๋ฆฌ๊ณ  ์•ต์ปค๋Š” ๋ฐ˜๋ณต์„ ํ”ผํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ •๊ทœ ํ‘œํ˜„์‹์€ /^((dog|cat|fish)(,|$))+$/

๋ฌธ์ž์—ด ์œ ํ˜•์ด ์ •๊ทœ์‹ /#[0-9]{6}/ ๋˜๋„๋ก ํ—ˆ์šฉํ•˜๊ณ  ์ •๊ทœ์‹ ${TColor} ์œ ํ˜•์„ ์ค‘์ฒฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type TColor = 'red' | 'blue' | /#[0-9]{6}/;
type TBorderValue = /[0-9]+px (solid|dashed) ${TColor}/

๊ฒฐ๊ณผ:

let border1: TBorderValue = '1px solid red'; // OK
let border2: TBorderValue = '1px solid yellow'; // TSError: .....

์‚ฌ์šฉ ์‚ฌ๋ก€: TypeScript typestyle "์œ ํ˜• ์•ˆ์ „" CSS ์Šคํƒ€์ผ์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉํ•  ๋ฉ”์„œ๋“œ๋ฅผ ๋…ธ์ถœํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์—์„œ ์ œ์•ˆํ•œ ๊ธฐ๋Šฅ์€ ํฌ๊ฒŒ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ œ์•ˆ๋œ ๋ฌธ์ž์—ด ์ •๊ทœ์‹ ์œ ํ˜•์€ ๋Œ€์‹  ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์ฝ”๋“œ๋ฅผ ์œ ํ˜• ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๊ณ  ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋›ฐ์–ด๋‚œ ์ธํ…”๋ฆฌ์„ผ์Šค๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@DanielRosenwasser @alexanderbird @Igmat : IMO ์ด ์ œ์•ˆ์€ TypeScript ๋ฐ ์›น ๊ฐœ๋ฐœ์˜ ํŒ๋„๋ฅผ ๋ฐ”๊ฟ€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌํ˜„์„ ๋ง‰๊ณ  ์žˆ๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํ™•์žฅ์„ฑ๊ณผ ์œ ํ˜• ๋ฐฉ์ถœ์ด ๋‚˜๋จธ์ง€ ๊ธฐ๋Šฅ์„ ๋ฐฉํ•ดํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ธก๋ฉด์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ๊ฒฝ๋กœ๊ฐ€ ์—†์œผ๋ฉด ๋‚˜์ค‘์— ์žˆ์„ ๋•Œ ๊ตฌํ˜„ํ•˜์„ธ์š”.

๋ฌธ์ž์—ด์ด ์•„๋‹Œ UUID ์œ ํ˜•์„ ์ฐพ๊ณ ์ž ์—ฌ๊ธฐ์— ๋„์ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ ๋ฌธ์ž์—ด์„ ์ •์˜ํ•˜๋Š” ์ •๊ทœ์‹์„ ๊ฐ–๋Š” ๊ฒƒ์ด ๊ต‰์žฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + ์œ ํ˜•์˜ ์œ ํšจ์„ฑ์„ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•(Email.test ์˜ˆ์ œ)๋„ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@skbergam ๋‹ค์‹œ ํ•œ๋ฒˆ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ TS ํ”„๋กœ์ ํŠธ๋Š” ์ •๋ง ๊ฑฐ๋Œ€ํ•˜๊ณ  ์ €๋„ ์ž‘์—…์ด ์žˆ์–ด์„œ ์ง„ํ–‰์ด ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€ TS ํ™•์žฅ์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ๋„์›€์„ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค...

๋‘ ๊ฐœ์˜ ๋™์ผํ•˜์ง€ ์•Š์€ ์ •๊ทœ ํ‘œํ˜„์‹ ๊ฐ„์— ํ•˜์œ„ ์œ ํ˜•/ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ๋ช…๋ชฉ ์œ ํ˜•์„ ํšจ๊ณผ์ ์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์ ์€ ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค.

@RyanCavanaugh ์ด์ „์— @maiermic์ด ๋Œ“๊ธ€์„ ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์ด ๋ฌธ์ œ๋Š” ๋‹คํ•ญ์‹ ์‹œ๊ฐ„์— ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(์ •๊ทœ ํ‘œํ˜„์‹์— ๋Œ€ํ•œ ํฌํ•จ ๋ฌธ์ œ ์ฐธ์กฐ).

๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์ •๊ทœ ํ‘œํ˜„์‹ ๊ด€๊ณ„๊ฐ€ ๋งŽ์ง€ ์•Š๊ธฐ๋ฅผ ํฌ๋งํ•˜์ง€๋งŒ ๊ฒฐ์ฝ” ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์œ ํ˜• ๊ฒ€์‚ฌ์™€ ๊ด€๋ จํ•˜์—ฌ ์ •๊ทœ ํ‘œํ˜„์‹ ๋ณต์ œ๊ฐ€ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๊ณ  typeof a const๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ(์˜ˆ: .d.ts ํŒŒ์ผ) TS๋Š” valueof e ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๊นŒ? e iff e ์˜ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์€ ๋ฆฌํ„ฐ๋Ÿด์ด๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค( undefined ์™€ ๊ฐ™์€ ๊ฒƒ์„ ๋‚ด๋ณด๋ƒ„)?

@maxlk ๋˜ํ•œ ์ฃผ์ œ์—์„œ ๋ฒ—์–ด /^((dog|cat|fish)(,(?=\b)|$))+$/ with test https://regex101.com/r/AuyP3g/1. ์ด๊ฒƒ์€ ์‰ผํ‘œ ๋’ค์˜ ๋‹จ์–ด ๋ฌธ์ž์— ๋Œ€ํ•ด ๊ธ์ •์ ์ธ lookahead๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DRY ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์ „์„ ๊ฐ•์ œ๋กœ ์žฌ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”!
์ด ์ƒํƒœ๋Š” ์–ด๋–ป์Šต๋‹ˆ๊นŒ?
๊ฐ€๊นŒ์šด ์‹œ์ผ ๋‚ด์— ์ด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๊นŒ? ๋กœ๋“œ๋งต์—์„œ ์ด์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@lgmat lib.d.ts ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ •์˜๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ๋ฌธ์„ ํ•œ ์ค„ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ์ œํ•œํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

๊ทธ ๋†€๋ผ์šด ๊ฐœ์„  ์‚ฌํ•ญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์ ์–ด๋„ ์•ŒํŒŒ ๋ฆด๋ฆฌ์Šค์—์„œ๋Š”?

์ •๊ทœ์‹ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์œ ํ˜•์€ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์— ์ ํ•ฉํ•˜๋ฉฐ ํ•˜๋“œ ์ฝ”๋”ฉ๋œ ์ž…๋ ฅ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋„ ์ข‹์Šต๋‹ˆ๋‹ค.

+1. ์šฐ๋ฆฌ์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋งค์šฐ ์ผ๋ฐ˜์ ์ด๋ฉฐ 'dd/mm/YYYY'์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด ๋‚ ์งœ ํ˜•์‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ œ์•ˆ๋œ ๋Œ€๋กœ ๋งค์šฐ ๋ฉ‹์ง„ ๊ธฐ๋Šฅ์ด ๋  ์ˆ˜ ์žˆ์ง€๋งŒ ์ž ์žฌ๋ ฅ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค.

  • ์ถœ๋ ฅ์€ ํ•ญ์ƒ ์ŠคํŒ…์ด์ง€๋งŒ(์ปดํŒŒ์ผ ์‹œ๊ฐ„์„ ํ™•์ธํ–ˆ์ง€๋งŒ) ๊ตฌ์กฐํ™”๋œ ๊ฐœ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ๋ฌธ๋ฒ•์€ regexp๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ์ œํ•œ๋˜๊ณ  ๋งŽ์€ ๊ฒƒ์„ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ •๊ทœ ํ‘œํ˜„์‹์˜ ๋ฌธ์ œ๋Š” ์ •๊ทœ ํ‘œํ˜„์‹์ด๊ณ  ๊ฒฐ๊ณผ๋Š” ํŠธ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ชฉ๋ก์ด๋ฉฐ ์ž„์˜์˜ ๊ธด ์ค‘์ฒฉ ์ˆ˜์ค€์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ํŒŒ์„œ๋Š” ์ œํ•œ์ ์ด๋ฉฐ ํ™•์žฅํ•  ์ˆ˜ ์—†๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์œผ๋กœ ํ‘œํ˜„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์€ #21861์—์„œ ์ œ์•ˆํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๊ตฌ๋ฌธ ๋ถ„์„์„ ์•„์›ƒ์†Œ์‹ฑํ•˜๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์œ„์˜ ๋ชจ๋“  ๊ฒƒ์ด ๋” ๊ฐ€ํŒŒ๋ฅธ ํ•™์Šต ๊ณก์„ ์˜ ๋Œ€๊ฐ€๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์ง€๋งŒ ์ด๋ด! regexp ๊ฒ€์‚ฌ๋Š” ๊ทธ ์œ„์— ๊ตฌํ˜„๋˜์–ด ์›๋ž˜ ์ œ์•ˆ์ด ์—ฌ์ „ํžˆ ์œ ์ง€๋˜๊ณ  ๋” ๊ณ ๊ธ‰ ๊ธฐ๊ณ„์—์„œ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋งํ–ˆ๋“ฏ์ด ๋” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ๊ตฌ๋ฌธ ๊ณต๊ธ‰์ž์ž…๋‹ˆ๋‹ค. #21861

์˜ˆ:

const uri: via URIParserAndEmitter = http://google.com; 
console.log(uri); // --> { protocol: 'http', host: 'google.com', path: undefined, query: undefined, hash: undefined }

const a: via PositiveNumberParser = 10; // --> 10
const b: via PositiveNumberParser = -10; // --> error

const date: via DateParser = 1/1/2019; // --> new Date(2019, 1, 1)


@lgmat lib.d.ts ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ •์˜๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ๋ฌธ์„ ํ•œ ์ค„ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋กœ ์ œํ•œํ•˜๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

@zspitz ๋Š” ๊ทธ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ˆ์ „์„ ์œ„ํ•ด ๊ธˆ์ง€๋˜์–ด ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์„ ๋ถˆํ–‰ํ•˜๊ฒŒ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ ๋†€๋ผ์šด ๊ฐœ์„  ์‚ฌํ•ญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์ ์–ด๋„ ์•ŒํŒŒ ๋ฆด๋ฆฌ์Šค์—์„œ๋Š”?

๋‚ด๊ฐ€ ์•„๋Š” ํ•œ ์ด๊ฒƒ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹์ œ์•ˆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. @gtamas , @AndrewEastwood

๋˜ํ•œ #11152๊ฐ€ ์ด๊ฒƒ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@Igmat ๊ท€ํ•˜์˜ ์ œ์•ˆ์€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๋ฌธ์ž์—ด ์œ ํ˜•์—์„œ๋งŒ ์œ ์šฉํ•˜๋„๋ก ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. @rylphs ์ œ์•ˆ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์„ธ์š”? ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ๊ธฐ๋ณธ ์œ ํ˜•์— ๋Œ€ํ•ด ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

type ColorLevel = (n:number) => n>0 && n<= 255
type RGB = {red:ColorLevel, green:ColorLevel, blue:ColorLevel};
let redColor:RGB = {red:255, green:0, blue:0}   //OK
let wrongColor:RGB = {red:255, green:900, blue:0} //wrong

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ธฐ๋ณธํ˜•์„ ๋„˜์–ด ๊ธฐ๋ณธํ˜•์ด ์•„๋‹Œ ์œ ํ˜•์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๊ฒƒ์ด ๋„ˆ๋ฌด ๋งŽ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฃผ์š” ๋ฌธ์ œ๋Š” ๋ณด์•ˆ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ผ๋ถ€ ์•…์„ฑ ์ฝ”๋“œ๋Š” ์œ ํ˜•์„ ํ™•์ธํ•˜๋Š” ๋™์•ˆ ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ๊ณผ ๊ด€๋ จํ•˜์—ฌ ๋งŽ์€ ์ƒŒ๋“œ๋ฐ•์‹ฑ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฐจ๋ผ๋ฆฌ ๋ฌธ์ž์—ด๊ณผ ์ˆซ์ž์— ๋Œ€ํ•œ ๋‘ ๊ฐ€์ง€ ์†”๋ฃจ์…˜์„ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

RegExp๋Š” ์ด๋ฅผ ์•…์˜์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์—ญ์ถ”์  ํ‘œํ˜„์‹ ์„ ๋งŒ๋“œ๋Š”

ํ•œ ๊ฐ€์ง€ ์ ์€ @DanielRosenwasser ๊ฐ€ ๋‹ค์–‘ํ•œ ์ •๊ทœ์‹ ์—”์ง„ ๊ตฌํ˜„์— ๋Œ€ํ•ด ์ œ๊ธฐํ•œ ๋ฌธ์ œ๊ฐ€

๊ทธ๊ฒƒ์€ ์‚ฌ์‹ค์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ข‹์ง€ ์•Š์ง€๋งŒ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ํ•„์š”ํ•œ regExp์˜ "ํ˜„๋Œ€์ ์ธ" ๋ถ€๋ถ„์„ ์ง€์ •ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋…ธ๋“œ ์—์„œ ์ž‘๋™ํ•˜๋Š” ์ผ๋ฐ˜(ES3์ž…๋‹ˆ๊นŒ?) regexp๋กœ ๊ธฐ๋ณธ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด ํ”Œ๋ž˜๊ทธ์™€ lookbehind ์–ด์„ค์…˜์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

const unicodeMatcher = /\u{1d306}/u;
let value: typeof unicodeMatcher;
function(input: string) {
  value = input;  // Invalid
  if (input.match(unicodeMatcher)) {
    value = input;  // OK
  }
}

์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๊ธ‰ ํ”Œ๋ž˜๊ทธ๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•œ ๊ฒฝ์šฐ.

let value: typeof unicodeMatcher = '๐Œ†';  // Warning, string literal isn't checked, because `variable` is of type `/\u{1d306}/u`.

์•Œ๋ ค์ฃผ์ง€ ์•Š์œผ๋ฉด TypeScript๋Š” ๊ณ ๊ธ‰ RegExp๋ฅผ ํ‰๊ฐ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€, ๊ณ ๊ธ‰ RegExp ๊ฒ€์‚ฌ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•˜๋Š” ๊ฒฝ๊ณ ๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๊ธ‰ ํ”Œ๋ž˜๊ทธ๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ณ  ๊ทธ์˜ ๋…ธ๋“œ๊ฐ€ ์ด๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ.

let value: typeof unicodeMatcher = '๐Œ†';  // OK

์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๊ธ‰ ํ”Œ๋ž˜๊ทธ๋กœ ํ”Œ๋ž˜๊ทธ๋ฅผ ํ™œ์„ฑํ™” ํ•˜๊ณ  ๊ทธ์˜ ๋…ธ๋“œ๊ฐ€ ์ด๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ.

let value: typeof unicodeMatcher = '๐Œ†';  
// Error, NodeJS does not support advanced RegExp, upgrade NodeJS to version X.Y.Z, or disable advanced RegExp checking.

ํ•ฉ๋ฆฌ์ ์ธ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
ํ”„๋กœ๊ทธ๋ž˜๋จธ ํŒ€์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋™์ผํ•œ ๋ฒ„์ „์˜ NodeJS๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋ชจ๋“  ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ์ตœ์‹  ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ์„ ์œ„ํ•ด ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์†”๋กœ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ฆ‰์„์—์„œ ์‰ฝ๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ,

์ด ๋ฌธ์ œ์˜ ํ˜„์žฌ ์ƒํƒœ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? TypeScript๊ฐ€ ์ด๋ ‡๊ฒŒ ์—„์ฒญ๋‚œ ์ž ์žฌ๋ ฅ๊ณผ ์ˆ˜์‹ญ ๊ฐ€์ง€์˜ ๋ฉ‹์ง„ ์ œ์•ˆ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์ด ์ฐธ์œผ๋กœ ์•ˆํƒ€๊น์Šต๋‹ˆ๋‹ค.

AFAIK์˜ ์›๋ž˜ ์ œ์•ˆ ์€ ํ•„์š”ํ•˜์ง€ ์•Š๊ณ  ์‹ค์ œ๋กœ ํ•„์š”ํ•˜์ง€ ์•Š์€ Emit ๊ฐœ์š” ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์ข‹์•˜์œผ๋ฏ€๋กœ ์ œ์•ˆ์„ ์ฐจ๋‹จํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ฌธ์ œ๋Š” ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด(๋ฌธ์ž์—ด ๋ฐ ์ˆซ์ž ๋ฆฌํ„ฐ๋Ÿด๊ณผ ํšจ๊ณผ์ ์œผ๋กœ ๋™์ผํ•˜๋ฏ€๋กœ ์–ด๋ ต์ง€ ์•Š์•„์•ผ ํ•จ)๊ณผ ์œ ํ˜• ์—ฐ์‚ฐ์ž patternof ( typeof ์™€ ์œ ์‚ฌ)์˜ ๋„์ž…์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค keyof ), ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๊ณ  _validated string_ ์œ ํ˜•์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type letterExpression = /[a-zA-Z]/;
let exp: letterExpression;
exp = /[a-zA-Z]/; // works
exp = /[A-Za-z]/; // error, the expressions do not match

type letter = patternof letterExpression;
type letter = patternof /[a-zA-Z]/; // this is equivalent

let a: letter;
a = 'f'; // works
a = '0'; // error
const email = /some-long-email-regex/;
type email = patternof typeof email;

declare let str: string;
if (str.match(email)) {
  str // typeof str === email
} else {
  str // typeof str === string
}

@m93a ์ดˆ๊ธฐ ์ œ์•ˆ์„ ํ•  ๋•Œ ์ถ”๊ฐ€ ์œ ํ˜• ์—ฐ์‚ฐ์ž๋กœ ์ด๋Ÿฌํ•œ ์†”๋ฃจ์…˜์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์œ ํ˜•์— ์˜ํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ฐฉ์ถœ ์˜ํ–ฅ์„ ์ œ๊ฑฐํ•˜๋Š” ์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋” ์žฅํ™ฉํ•ด ๋ณด์ด์ง€๋งŒ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ์ƒˆ๋กœ์šด ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ธฐ ์œ„ํ•ด ์ด ์ œ์•ˆ์„ ํ™•์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

4๋‹จ๊ณ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. regexp-validated string literal ์œ ํ˜• ์ถ”๊ฐ€:
    TypeScript type Email = /some-long-email-regex/;
  2. core lib์˜ RegExp ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ผ๋ฐ˜์œผ๋กœ ๋ณ€๊ฒฝํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
    TypeScript interface RegExp<T extends string = string> { test(stringToTest: string): stringToTest is T; }
  3. ์‹ค์ œ ์ฝ”๋“œ์—์„œ ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•œ ์œ ํ˜• ์œ ์ถ” ๋ณ€๊ฒฝ:
    TypeScript const Email = /some-long-email-regex/; // infers to `RegExp</some-long-email-regex/>`
  4. InstanceType ์™€ ๊ฐ™์€ conditional types ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์œ ํ˜• ๋„์šฐ๋ฏธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    TypeScript type ValidatedStringType<T extends RegExp> = T extends RegExp<infer V> ? V : string;

์‚ฌ์šฉ ์˜ˆ:

const Email = /some-long-email-regex/;
type Email = ValidatedStringType<typeof Email>;

const email: Email = `[email protected]`; // correct
const email2: Email = `emexample.com`; // compile time error

let userInput: string;
if (Email.test(userInput)) {
    // `userInput` here IS of type `Email`
} else {
    // and here it is just `string`
}

@์ด๊ทธ๋งท ์ฟจ. ๊ท€ํ•˜์˜ ์ œ์•ˆ์€ TypeScript์— ๋Œ€ํ•ด ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋Š๊ปด์ง€๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์ด ๋œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์•„๋งˆ๋„ ์ข‹์€ ์ผ์ž…๋‹ˆ๋‹ค. ๋‚ด ์ œ์•ˆ์˜ ์œ ์ผํ•œ ์žฅ์ ์€ ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด์ด ๋ฌธ์ž์—ด ๋ฐ ์ˆซ์ž ๋ฆฌํ„ฐ๋Ÿด๊ณผ ๋™์ผํ•˜๊ฒŒ ๋Š๊ปด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ถ€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ํ˜ผ๋™์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

let a: 'foo' = 'foo'; // works
let b: 42 = 42; // works
let c: /x/ = /x/; // error

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ๋‹น์‹ ์˜ ์ œ์•ˆ์˜ ๋‹จ์ˆœํ•จ์ด ๋‹จ์ ์„ ๋Šฅ๊ฐ€ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํŽธ์ง‘: ๋‚˜๋Š” ValidatedStringType<R> ์˜ ๊ธธ์ด๊ฐ€ ์ •๋ง ๋งˆ์Œ์— ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ํŒจํ„ด ์„ ํ˜ธ์ถœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค๋ฉด ๊ฒฐ๊ตญ PatternOf<R> ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์œ ํ˜•์ด ์ž…๋ ฅํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฐ๋‹ค๋Š” ๋ง์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์€ ์ฒ˜์Œ ์„ธ ๊ธ€์ž๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ํƒญ์„ ๋ˆ„๋ฅด๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ง€ ๋” ํฐ ์ฝ”๋“œ ์ŠคํŒŒ๊ฒŒํ‹ฐ์ผ€์ด์…˜ ์˜ํ–ฅ์ด ์žˆ์„ ๋ฟ์ž…๋‹ˆ๋‹ค.

@Igmat ๋‹น์‹ ์˜ ์†”๋ฃจ์…˜์€ ๊ฐœ๋ฐœ ์‹œ์ ์—์„œ ํ›Œ๋ฅญํ•˜์ง€๋งŒ ๊ฐ€๋…์„ฑ์ด ์˜ฌ๋ผ๊ฐˆ์ˆ˜๋ก @m93a๊ฐ€ ์ œ์•ˆํ•œ ๊ฐ€๋Šฅ์„ฑ์ด ํ›จ์”ฌ ๋” ๋‚˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ๋„ ๊ฑฐ์˜ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์ตœ๋Œ€ํ•œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@Akxe ์ €๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋งค์šฐ ๊ตฌ์ฒด์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ๋Š” ๋‹ค๋ฅธ ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@RyanCavanaugh ์ด์— ๋Œ€ํ•œ ๊ท€ํ•˜์˜ ์˜๊ฒฌ์„ ๋ง์”€ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (ํŠนํžˆ ์›๋ž˜ ์ œ์•ˆ์„œ ์™€ ๋งˆ์ง€๋ง‰ 4๊ฐœ์˜ ์˜๊ฒฌ(์ด๊ฒƒ์€ ์ œ์™ธ).) ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! :+1:

๋ฌธ์ž์—ด์— ๋Œ€ํ•œ ์ผ๋ฐ˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ธฐ๋ณธ๊ฐ’์€ .* ์ž…๋‹ˆ๊นŒ?

let a: 'foo' = 'foo'; // works
let b: 42 = 42; // works
let c: /x/ = /x/; // works
let d: string<x.> = 'xa'; // works

๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด 'foo' ๋Š” string<foo> ์˜ ์„คํƒ•์œผ๋กœ ๊ฐ„์ฃผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ValidatedStringType<R> ์˜ ๊ธธ์ด๊ฐ€ ์ •๋ง ๋งˆ์Œ์— ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด _patterns_๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค๋ฉด ๊ฒฐ๊ตญ PatternOf<R> ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@m93a , IMO, ์ด ๊ฒฝ์šฐ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” InstanceType ๋ฐ ReturnType ๋„์šฐ๋ฏธ์™€ ์ผ์น˜ํ•˜๋„๋ก PatternType<R> ๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

@amir-arad, ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ interface RegExp ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

@RyanCavanaugh ๋„์›€์ด๋œ๋‹ค๋ฉด ์ƒˆ๋กœ ์ฐพ์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์›๋ž˜ ์ œ์•ˆ์„ ๋‹ค์‹œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

@amir-arad ์ œ์•ˆํ•œ ๊ตฌ๋ฌธ์ด ๋‚˜๋จธ์ง€ TypeScript์™€ ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ํ˜•์‹์„ ์ž„์˜์˜ ํ‘œํ˜„์‹์ด ์•„๋‹Œ ์ผ๋ฐ˜ ์ธ์ˆ˜๋กœ๋งŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ์•ˆํ•œ ๊ตฌ๋ฌธ์€ ๋งค์šฐ ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ˜•์‹์„ ์ทจํ•˜๊ณ  ํ˜•์‹์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ธ ๊ฒƒ์ฒ˜๋Ÿผ ์ œ๋„ค๋ฆญ ํ˜•์‹์„ ์ƒ๊ฐํ•˜์‹ญ์‹œ์˜ค. ๋‹ค์Œ ๋‘ ์ฝ”๋“œ ์กฐ๊ฐ์€ ์˜๋ฏธ์™€ ๊ตฌ๋ฌธ ๋ชจ๋‘์—์„œ ๋งค์šฐ ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

function foo(str: string) {
  return str === 'bar' ? true : false
}

type foo<T extends string> = T extends 'bar' ? true : false;

์ƒˆ ๊ตฌ๋ฌธ์€ JavaScript์˜ ์ •๊ทœ์‹์ด let all = String(.*) ๋กœ ์ž‘์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ œ์•ˆํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ ๊ตฌ๋ฌธ์„ ๋ชป์“ฐ๊ฒŒ ๋‚จ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋‚˜๋Š” ๋‹น์‹ ์˜ ์ œ์•ˆ์ด ๋ณ„๋กœ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@ m93a ๋‚ด ์ œ์•ˆ์€ ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•„๋‹Œ ์œ ํ˜• ์ฃผ์„์— ๋Œ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด ๋จธ๋ฆฌ ๊ผญ๋Œ€๊ธฐ์—์„œ @Igmat ,

interface RegExp {
    test(stringToTest: string): stringToTest is string<this>;
}

@amir-arad, ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ์ œ์•ˆ์— ๋” ๊ท€์ค‘ํ•œ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ string ๋Š” ๋งค์šฐ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ณธ ์š”์†Œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์–ธ๋œป ๋ณด๊ธฐ์—๋Š” ์ „์ฒด TS ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•œ ๋งค์šฐ ์ค‘์š”ํ•œ ๋ณ€๊ฒฝ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.

๋ช…๋ฐฑํ•œ ๋ฌธ์ œ๊ฐ€ ๋ณด์ด์ง€๋Š” ์•Š์ง€๋งŒ ๊ทธ๋Ÿฌํ•œ ์ œ์•ˆ์€ ํ›จ์”ฌ ๋” ์ž์„ธํ•˜๊ณ  ๊ธฐ์กด์˜ ๋งŽ์€ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๋ชฉ์ ์— ๋Œ€ํ•œ ์ ์ ˆํ•œ ์ •๋‹น์„ฑ์„ ํฌํ•จํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
๋‹น์‹ ์˜ ์ œ์•ˆ์€ ํ•˜๋‚˜์˜ ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•˜๊ณ  ํ•˜๋‚˜์˜ ๊ธฐ๋ณธ ์œ ํ˜•์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ˜๋ฉด ๋‚ด ์ œ์•ˆ์€ ํ•˜๋‚˜์˜ ์œ ํ˜•๋งŒ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋ถˆํ–‰ํžˆ๋„ ์ €๋Š” ๊ทธ๋Ÿฌํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ œ์•ˆ์„ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์„ ํ• ์• ํ•  ์ค€๋น„๊ฐ€ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค(๋˜ํ•œ TS์˜ ๋ชจ๋“  ์ œ์•ˆ์ด ์ƒ๋‹นํ•œ ์ง€์—ฐ ์—†์ด ๊ตฌํ˜„๋˜์ง€๋Š” ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค). ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋‚ด ํ”ผ๋“œ๋ฐฑ์„ ๊ธฐ๊บผ์ด ์ œ๊ณตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ regexp ์œ ํ˜•์ด ์‹ค์ œ ์ •๊ทœ์‹(์ •๊ทœ๊ฐ€ ์•„๋‹Œ Perl๊ณผ ๊ฐ™์€ ์ •๊ทœ์‹์ด ์•„๋‹˜)์ธ ๊ฒฝ์šฐ ์ด๋ฅผ ๊ฒฐ์ •๋ก ์  FSM์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์—ฌ๊ธฐ์— ๋Œ€ํ•ด ๋ฐ์นด๋ฅดํŠธ ๊ณฑ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ๊ฒฐํ•ฉ ๋ฐ ๋ถ„๋ฆฌ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ •๊ทœ์‹์€ ๋ถ€์šธ ์—ฐ์‚ฐ์—์„œ ๋‹ซํž™๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์ด ์›์ž์ ์ด์ง€ ์•Š๊ณ  ์ปดํŒŒ์ผ ํƒ€์ž„ ๋ฌธ์ž ๋ชฉ๋ก์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ๊ฒฝ์šฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ชจ๋“  ์—ฐ์‚ฐ์ž๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ด ์•ฝ๊ฐ„ ์ €ํ•˜๋  ๋ฟ์ž…๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์‹ค์ˆ˜๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

Mithril์ด ์ด๊ฒƒ๋“ค์„ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•˜๊ธฐ ์œ„ํ•ด ๋“ค๋ฅด๋ฉด, ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ์— Type-safe๊ฐ€ ๊ทธ๊ฒƒ ์—†์ด๋Š” ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ์™€ JSX ๊ตฌ๋ฌธ ๋ชจ๋‘์˜ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. (์šฐ๋ฆฌ๋Š” ๋‘˜ ๋‹ค ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.)

  • ์šฐ๋ฆฌ์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ oninit , oncreate , onbeforeupdate , onupdate , onbeforeremove ๋ฐ onremove ์—๋Š” ๊ณ ์œ ํ•œ ํ›„ํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠน๋ณ„ํ•œ ํ”„๋กœํ† ํƒ€์ž….
  • DOM vnode์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ง ๊ทธ๋Œ€๋กœ on ์‹œ์ž‘ํ•˜๋Š” ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ์ด๋ฉฐ, ์šฐ๋ฆฌ๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ํ•จ์ˆ˜์™€ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๊ฐ์ฒด( handleEvent ๋ฉ”์†Œ๋“œ ์‚ฌ์šฉ)๋ฅผ ๋ชจ๋‘ ์ง€์›ํ•˜๋ฉฐ addEventListener removeEventListener .
  • ์ ์ ˆํ•œ ๊ฒฝ์šฐ ํ‚ค์™€ ์ฐธ์กฐ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ์€ ์ง€์› DOM ๋…ธ๋“œ ์ž์ฒด์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ์†์„ฑ ๋˜๋Š” ์†์„ฑ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜• + ์œ ํ˜• ๋ถ€์ •์„ ์‚ฌ์šฉ ํ•˜์—ฌ DOM vnode์— ๋Œ€ํ•ด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface BaseAttributes {
    // Lifecycle attributes
    oninit(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    oncreate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeupdate(
        vnode: Vnode<this, Vnode<Attributes, []>>,
        old: Vnode<this, Vnode<Attributes, []>>
    ): void;
    onupdate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeremove(vnode: Vnode<this, Vnode<Attributes, []>>): void | Promise<void>;
    onremove(vnode: Vnode<this, Vnode<Attributes, []>>): void;

    // Control attributes
    key: PropertyKey;
}

interface DOMAttributes extends BaseAttributes {
    // Event handlers
    [key: /^on/ & not keyof BaseAttributes]: (
        ((this: Element, ev: Event) => void | boolean) |
        {handleEvent(ev: Event): void}
    );

    // Other attributes
    [key: keyof HTMLElement & not keyof BaseAttributes & not /^on/]: any;
    [key: string & not keyof BaseAttributes & not /^on/]: string;
}

interface ComponentAttributes extends BaseAttributes {
    // Nothing else interesting unless components define them.
}

(์ด๋Ÿฌํ•œ ์ •๊ทœ์‹์—์„œ ๊ทธ๋ฃน์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋„ ์ข‹์ง€๋งŒ ์ˆจ์„ ์‰ฌ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.)

ํŽธ์ง‘: ์ œ์•ˆ์„œ์—์„œ ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์„ธ๋ถ€ ์‚ฌํ•ญ์„ ๋ช…ํ™•ํžˆ ํ•˜์‹ญ์‹œ์˜ค.
ํŽธ์ง‘ 2: ์‹ค์ œ๋กœ ์ˆ˜ํ•™์ ์œผ๋กœ ์ •ํ™•ํ•˜๋„๋ก ํ…Œํฌ๋‹ˆ์ปฌ ๋น„ํŠธ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
ํŽธ์ง‘ 3: ๋‹จ์ผ ๋ฌธ์ž ๊ณต์šฉ์ฒด์˜ ์ผ๋ฐ˜ ์ฃผ์—ฐ ์ง€์› ์ถ”๊ฐ€

๋‹ค์Œ์€ ์ด ๋ฌธ์ œ๋ฅผ ํ›จ์”ฌ ๋” ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ธ ์ œ์•ˆ์ž…๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ์ „์ฒด ์ •๊ทœ ํ‘œํ˜„์‹์€ ๋‹ค๋ฅธ ์œ ํ˜•๊ณผ ํ•ฉ์น˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋งˆ๋„ ์ข‹์€ ์ƒ๊ฐ์ด ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์ด ๋” ๋‚˜์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • `value` - ์ด๊ฒƒ์€ ๋ง ๊ทธ๋Œ€๋กœ "value" ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
  • `value${"a" | "b"}` - ์ด๊ฒƒ์€ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ "valuea" | "valueb" ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
  • `value${string}` - ์ด๊ฒƒ์€ regexp /^value/ ์™€ ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ "value" , "valuea" ๋ฐ "valueakjsfbf aflksfief fskdf d" ๋ชจ๋‘ ํ• ๋‹น ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • `foo${string}bar` - ์ด๊ฒƒ์€ regexp /^foo.*bar$/ ์™€ ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ ์ •๊ทœํ™”ํ•˜๊ธฐ๊ฐ€ ์กฐ๊ธˆ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ๋ฌผ๋ก  ์—ฌ๋Ÿฌ ๋ณด๊ฐ„์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. `foo${string}bar${string}baz` ์€ ์œ ํšจํ•œ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์ž…๋‹ˆ๋‹ค.
  • ๋ณด๊ฐ„ ์€ string ํ™•์žฅ ํ•ด์•ผ ํ•˜๋ฉฐ ์žฌ๊ท€์ ์ด์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. (๋‘ ๋ฒˆ์งธ ์กฐ๊ฑด์€ ๊ธฐ์ˆ ์ ์ธ ์ด์œ ์ž…๋‹ˆ๋‹ค.)
  • ํ…œํ”Œ๋ฆฟ์€ ์ž…๋ ฅ ๋ฌธ์ž A ์ž…๋ ฅํ•˜๋Š” ๋ฌธ์ž ํ…œํ”Œ๋ฆฟ์— ํ• ๋‹น ํ•  B ๋ฌธ์ž์—ด์˜ ์ง‘ํ•ฉ์— ํ• ๋‹น ํ•  ๊ฒฝ์šฐ ๋งŒ A ์ŠคํŠธ๋ง์˜ ์„ธํŠธ์˜ ์„œ๋ธŒ ์„ธํŠธ์— ํ• ๋‹น ํ•  B .

์œ„์— ์ถ”๊ฐ€ํ•˜์—ฌ T ๋Š” ๋‹จ์ผ ๋ฌธ์ž ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์œผ๋กœ๋งŒ ๊ตฌ์„ฑ๋˜์–ด์•ผ ํ•˜๋Š” ํŠน์ˆ˜ starof T ์œ ํ˜•์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. string ๋Š” starof (...) ์˜ ์œ ํ˜• ๋ณ„์นญ์œผ๋กœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ... ๋Š” lone์„ ํฌํ•จํ•˜์—ฌ U+0000์—์„œ U+FFFF๊นŒ์ง€์˜ ๋ชจ๋“  ๋‹จ์ผ UCS-2 ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ํ•ฉ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ฆฌ์ธ. ์ด๋ฅผ ํ†ตํ•ด ES base-10 ์ˆซ์ž ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•œ ์ „์ฒด ๋ฌธ๋ฒ•์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

type DecimalDigit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type Decimal = `${DecimalDigit}{starof DecimalDigit}`

type Numeric = `${(
    | Decimal
    | `${Decimal}.${starof DecimalDigit}`
    | `.${Decimal}`
)}${"" | (
    | `E${Decimal}` | `E+${Decimal}` | `E-${Decimal}`
    | `e${Decimal}` | `e+${Decimal}` | `e-${Decimal}`
)}`

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํŠน์ • ๋‚ด์žฅ ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ ํ˜•์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Number.prototype.toString(base?) - ์ด๊ฒƒ์€ ์ •์ ์œผ๋กœ ์•Œ๋ ค์ง„ base ๋Œ€ํ•ด ์œ„์˜ Numeric ์œ ํ˜• ๋˜๋Š” ์ผ๋ถ€ ๋ณ€ํ˜•์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • +x , x | 0 , parseInt(x) ๋ฐ ์œ ์‚ฌ - x ๊ฐ€ ์œ„์— ์ •์˜๋œ ๋Œ€๋กœ Numeric ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ง„ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ ์œ ํ˜•์€ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌํ„ฐ๋Ÿด ์ˆซ์ž ์œ ํ˜•์œผ๋กœ ์ ์ ˆํ•˜๊ฒŒ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ Key extends `on${infer EventName}` ? EventTypeMap[TagName][EventName] : never ์™€ ๊ฐ™์ด ์ผ์น˜ํ•˜๋Š” ๊ทธ๋ฃน์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ ์ถ”์ถœ์€ ํ•ญ์ƒ ์ „์ฒด ์ด๋ฆ„์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฏ€๋กœ ์ž„์˜ ํฌํ•จ์„ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด ${string} ๋ณด๊ฐ„์„ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์š•์‹ฌ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ` "foo.bar.baz" extends ${infer T}.${infer U} ? [T, U] : never ๋Š” ["foo", "bar.baz"] ์ด ์•„๋‹ˆ๋ผ ["foo.bar", "baz"] ["foo", "bar.baz"] ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.


๊ธฐ์ˆ ์ ์ธ ๊ด€์ ์—์„œ ์ด๊ฒƒ์€ ์›์‹œ ์ •๊ทœ ํ‘œํ˜„์‹๋ณด๋‹ค ๊ตฌํ˜„ํ•˜๊ธฐ์— ํ›จ์”ฌ ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. JS ์ •๊ทœ ํ‘œํ˜„์‹์€ ๊ทœ์น™์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ญ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋ฉฐ ๋‹ค์Œ ํ˜•์‹์œผ๋กœ ๋งŽ์€ ๋ณต์žก์„ฑ์„ ์ˆ˜๋ฐ˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์žฌ๊ท€๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ํ•œ ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์€ ๊ฐ๊ฐ ํ•˜๋‚˜์˜ ์ •๊ทœ ์–ธ์–ด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์ด๋ก ๊ณผ ๋งค์šฐ ๋ฐ€์ ‘ํ•˜๊ฒŒ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ์ง€์›ํ•จ).

  • ๋นˆ ์–ธ์–ด: ""
  • ์กฐํ•ฉ: "a" | "b"
  • ์—ฐ๊ฒฐ: `${a}${b}`
  • Kleene ๋ณ„(์ผ๋ถ€): starof T ( T ์—๋Š” ๋‹จ์ผ ๋ฌธ์ž ๋ฐ ๊ณต์šฉ์ฒด๋งŒ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

์ด ๋ฌธ์ž์—ด์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ ํ™•์ธ ํ•˜์œ„ ์œ ํ˜• ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ์„œ๋ธŒ ๊ทธ๋ž˜ํ”„์˜ ๋™ํ˜• ๋ฌธ์ œ๊ฐ€ ์ตœ์•…์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ, ๊ทธ๋Ÿฌ๋‚˜ ์•ฝ๊ฐ„ ํฐ ๊ตฌ์† ์š”์ธ์€ ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค :

  1. ์ง€๊ธˆ๊นŒ์ง€ ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ๋Š” ๋‚˜๋ฌด๋กœ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ์œ ํ•œ ๋ฌธ์ž์—ด์˜ ํ•ฉ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ž‘์—…ํ•˜๊ธฐ์— ๋น„๊ต์  ๋ถ„๋ช…ํ•ฉ๋‹ˆ๋‹ค. (์œ„์˜ ์ผ์น˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋ณต์žกํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋กœํ”„๋กœ ๊ฒฐํ•ฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹จ์ผ ๋ฌธ์ž ๊ฒฐํ•ฉ ๋ฐ ์œ ์‚ฌํ•œ ๋‹จ์ผ ๋ถ„ํ•  + ๊ฒฐํ•ฉ์œผ๋กœ ์ •๊ทœํ™”ํ•˜๋Š” ๊ฒƒ์€ ์™„๋ฒฝํ•˜๊ฒŒ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.)

  2. ์ „์ฒด ํ†ตํ•ฉ ์œ ํ˜•์„ ๋ฐฉํ–ฅ ๊ทธ๋ž˜ํ”„๋กœ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ:

    1. ์ด๋Ÿฌํ•œ ๋ฌธ์ž์˜ ๋ณ„ํ‘œ ๊ฒฐํ•ฉ์€ ์ƒ์œ„ ๋…ธ๋“œ๊ฐ€ ๊ฐ ๋ฌธ์ž ๋ฐ ํ•˜์œ„ ๊ทธ๋ž˜ํ”„์˜ ๊ฐ ํ•˜์œ„ ๋…ธ๋“œ ๋ชจ๋‘์— ๋Œ€ํ•ด ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๊ฐ–๊ณ  ๊ฐ ๋ฌธ์ž๊ฐ€ ๋‹ค๋ฅธ ๋ชจ๋“  ๋ฌธ์ž ๋ฐ ํ•˜์œ„ ๊ทธ๋ž˜ํ”„์˜ ๋ชจ๋“  ํ•˜์œ„ ๋…ธ๋“œ ๋ชจ๋‘์— ๋Œ€ํ•ด ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๊ฐ–๋Š” ํ•˜์œ„ ๊ทธ๋ž˜ํ”„์ž…๋‹ˆ๋‹ค.
    2. ๊ทธ๋ž˜ํ”„์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฐ€๋Šฅ์„ฑ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐฉํ–ฅ์„ฑ ํŠธ๋ฆฌ์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

    ์ด Math.SE ์ฑ„ํŒ…์— ๋”ฐ๋ฅด๋ฉด (์•ฝ ์—ฌ๊ธฐ ์—์„œ ์‹œ์ž‘) ์ด ๊ฒฐ๊ณผ ๊ทธ๋ž˜ํ”„์—๋Š” ๊ฒฝ๊ณ„๊ฐ€ ์žˆ๋Š” ์†(์ฆ‰, ๋‹ค๋ฅธ ๊ฐ€์žฅ์ž๋ฆฌ์— ๋Œ€ํ•œ ์œ ํ•œํ•œ ์ˆ˜์˜ ์ ํ”„*)์ด ์žˆ๊ณ  starof ์—†๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ์œ ํ˜• ํ‰๋“ฑ ์ด ์ด๋ฅผ ๊ฐ•๋ ฅํžˆ ์˜์‹ฌํ•ฉ๋‹ˆ๋‹ค. (์œ„์— ๋งํฌ๋œ Wikipedia ๊ธฐ์‚ฌ์—๋Š” "์•Œ๊ณ ๋ฆฌ์ฆ˜"์— ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๊ฐ€ ์žˆ์œผ๋ฉฐ ํŠน์ˆ˜ ๋Œ€์†Œ๋ฌธ์ž๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์„น์…˜์„ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.)


  3. ์ด๋Ÿฌํ•œ ํ‚ค ์ค‘ ์–ด๋Š ๊ฒƒ๋„ ์—ฌ๊ธฐ์— ์‹ค์ œ ๋Ÿฐํƒ€์ž„ ๋น„์šฉ ๋•Œ๋ฌธ์— ๋Œ€๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ๊ฒƒ๋“ค์— ์˜ํ•ด ์‹ค์ œ๋กœ ์ƒ๊ฐํ•˜๊ณ , ํฐ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ž‘์€ ํ‚ค์˜ ๊ฒฝ์šฐ ๋น ๋ฅด๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.


  4. ๋น„๊ต๋  ๋ชจ๋“  ํ•˜์œ„ ๊ทธ๋ž˜ํ”„๋Š” ๋ฃจํŠธ ๋…ธ๋“œ์ธ ์ ์–ด๋„ ํ•˜๋‚˜์˜ ๋…ธ๋“œ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. (์ด๊ฒƒ์€ ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.) ๋”ฐ๋ผ์„œ ์ด๊ฒƒ์€ ๊ทธ ์ž์ฒด๋กœ ๋ฌธ์ œ ๊ณต๊ฐ„์„ ๊ทน์ ์œผ๋กœ ์ค„์ด๊ณ  ๋‹คํ•ญ์‹ ์‹œ๊ฐ„ ๊ฒ€์‚ฌ๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.


๊ทธ๋ฆฌ๊ณ  ๋ฌผ๋ก  ์ด๋Ÿฌํ•œ ์œ ํ˜• ๊ฐ„์˜ ๊ต์ฐจ๋Š” ์‚ฌ์†Œํ•˜์ง€ ์•Š์ง€๋งŒ ์œ„์˜ ์ œํ•œ ์‚ฌํ•ญ์œผ๋กœ ์ธํ•ด ์œ ์‚ฌํ•œ ์ƒํ™˜ ์š”์†Œ๊ฐ€ ์กด์žฌํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ๋งˆ์ง€๋ง‰ ์ œํ•œ์€ ๋ถ„๋ช…ํžˆ ๋‹คํ•ญ์‹ ์‹œ๊ฐ„์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

* ์ˆ˜ํ•™์ ์œผ๋กœ ์†์€ ์šฐ๋ฆฌ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์—๊ฒŒ ๋‹ค์†Œ ์ง๊ด€์ ์ด์ง€ ์•Š๊ฒŒ ์ •์˜๋˜์ง€๋งŒ(์ ํ”„ ์—†์ด ๊ทธ๋ž˜ํ”„๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด ํ‘œ๋ฉด์— ์ฐ”๋Ÿฌ์•ผ ํ•˜๋Š” ์ตœ์†Œ ๊ตฌ๋ฉ ์ˆ˜), ์ œํ•œ๋œ ์†(๊ตฌ๋ฉ ์ˆ˜ ์ œํ•œ)์€ ์ œํ•œ๋œ ์ˆ˜์˜ ์ ํ”„๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. .

์ด ๊ตฌ์ฒด์ ์ธ ์ œ์•ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ฃผ์„์˜ ์˜ˆ๋ฅผ ๋ฒˆ์—ญํ•˜๋Š” ๋ฐฉ๋ฒ•์€

// This would work as a *full* type implementation mod implementations of `HTMLTypeMap` +
// `HTMLEventMap`
type BaseAttributes = {
    // Lifecycle attributes
    oninit(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    oncreate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeupdate(
        vnode: Vnode<this, Vnode<Attributes, []>>,
        old: Vnode<this, Vnode<Attributes, []>>
    ): void;
    onupdate(vnode: Vnode<this, Vnode<Attributes, []>>): void;
    onbeforeremove(vnode: Vnode<this, Vnode<Attributes, []>>): void | Promise<void>;
    onremove(vnode: Vnode<this, Vnode<Attributes, []>>): void;

    // Control attributes
    key: PropertyKey;
}

interface HTMLTypeMap {
    // ...
}

interface HTMLEventMap {
    // ...
}

// Just asserting a simple constraint
type _Assert<T extends true> = never;
type _Test0 = _Assert<
    keyof HTMLTypeMap[keyof HTMLTypeMap] extends `on${string}` ? false : true
>;

type EventHandler<Event> =
    ((this: Element, ev: Event) => void | boolean) |
    {handleEvent(ev: Event): void};

type Optional<T> = {[P in keyof T]?: T[P] | null | undefined | void}

type DOMAttributes<T extends keyof HTMLAttributeMap> = Optional<(
    & BaseAttributes
    & {[K in `on${keyof HTMLEventMap[T]}` & not keyof BaseAttributes]: EventHandler<(
        K extends `on${infer E}` ? HTMLEventMap[E] : never
    )>}
    & Record<
        keyof `on${string & not keyof HTMLEventMap}` & not keyof BaseAttributes,
        EventHandler<Event>
    >
    & Pick<HTMLTypeMap[T], (
        & keyof HTMLTypeMap[T]
        & not `on${string}`
        & not keyof BaseAttributes
    )>
    & Record<(
        & string
        & not keyof HTMLTypeMap[T]
        & not keyof BaseAttributes
        & not `on${string}`
    ), string | boolean>
)>;

ํŽธ์ง‘: ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Lodash์˜ _.get ๋ฉ”์„œ๋“œ ๋ฐ _.property(path) ๋ฉ”์„œ๋“œ ๋ฐ _.map(coll, path) ์•ฝ์‹๊ณผ ๊ฐ™์€ ์†์„ฑ ์•ฝ์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ด€๋ จ ๋ฉ”์„œ๋“œ์˜ 90%๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž…๋ ฅํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ ์ œ๊ฐ€ ์ƒ๊ฐํ•˜์ง€ ๋ชปํ•œ ๋‹ค๋ฅธ ๋ช‡ ๊ฐ€์ง€๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•„๋งˆ๋„ ๊ทธ๊ฒƒ์ด ์ œ๊ฐ€ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ํฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค. (ํ•ด๋‹น ์œ ํ˜•์˜ ๊ตฌํ˜„์€ ๋…์ž์—๊ฒŒ ์—ฐ์Šต ๋ฌธ์ œ๋กœ ๋‚จ๊ฒจ๋‘๊ฒ ์ง€๋งŒ {0: ..., 1: ...}[Path extends "" ? 0 : 1] ์™€ ๊ฐ™์ด ์ฆ‰์‹œ ์ธ๋ฑ์‹ฑ๋œ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” ์กฐ๊ฑด๋ถ€ ์œ ํ˜•์˜ ์ผ๋ฐ˜์ ์ธ ํŠธ๋ฆญ๊ณผ ๊ทธ ์กฐํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ ์ถ”์ฒœ์€ ์ •๊ทœ์‹ ์œ ํ˜•์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ ํ˜• ๊ณต๊ธ‰์ž ๊ตฌํ˜„์— ๋…ธ๋ ฅ์„ ์ง‘์ค‘ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ •๊ทœ์‹ ์œ ํ˜•์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๋Œ€์‹  ์œ ํ˜• ๊ณต๊ธ‰์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋•Œ๋ฌธ์—

  1. TypeScript์— ๋งŽ์€ ์ƒˆ๋กœ์šด ๊ฐ€๋Šฅ์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ์†”๋ฃจ์…˜์œผ๋กœ ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์œ ํ˜•์˜ ๊ฐ’์„ ๋ณด๋Š” ์‚ฌ๋žŒ๋“ค์„ ๋„˜์–ด ๋” ๋งŽ์€ ๊ฐœ๋ฐœ์ž ๊ทธ๋ฃน์˜ ์ง€์›์„ ๋” ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. typescript repo ์†Œ์œ ์ž๋Š” ์ด ์•„์ด๋””์–ด์— ์—ด๋ ค ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋ฉฐ ์˜ฌ๋ฐ”๋ฅธ ์ œ์•ˆ์„ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. #3136 ์ฐธ์กฐ

F#์—๋Š” ์˜คํ”ˆ ์†Œ์Šค ์ •๊ทœ์‹ ์œ ํ˜• ๊ณต๊ธ‰์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ ํ˜• ๊ณต๊ธ‰์ž์— ๋Œ€ํ•œ ์ผ๋ถ€ ์ •๋ณด: https://link.medium.com/0wS7vgaDQV

์œ ํ˜• ๊ณต๊ธ‰์ž๊ฐ€ ๊ตฌํ˜„๋˜๊ณ  ์ •๊ทœ์‹ ์œ ํ˜• ๊ณต๊ธ‰์ž๊ฐ€ ์˜คํ”ˆ ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ตฌํ˜„๋˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type PhoneNumber = RegexProvider</^\d{3}-\d{3}-\d{4}$/>
const acceptableNumber: PhoneNumber = "123-456-7890"; //  no compiler error
const unacceptableNumber: PhoneNumber = "hello world"; // compiler error

@AlexLeung ์ ์–ด๋„์ด ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

  • TypeScript๋Š” ๋ช…๋ชฉ์ ์œผ๋กœ ํƒ€์ดํ•‘๋œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ตฌ์กฐ์ ์œผ๋กœ ํƒ€์ดํ•‘๋˜์—ˆ์œผ๋ฉฐ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์กฐ์ž‘์„ ์œ„ํ•ด ๊ทธ ๊ตฌ์กฐ์  ์ •์‹ ์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ์œ ํ˜• ๊ณต๊ธ‰์ž๋Š” RegexProvider</^foo$/> ๊ฐ€ "foo" ์™€ ๋™๋“ฑํ•˜๊ฒŒ ์ทจ๊ธ‰ ๋˜์ง€ ์•Š๊ณ  ๊ทธ๊ฒƒ์˜ ๋ช…๋ชฉ ํ•˜์œ„ ์œ ํ˜•์ธ ๋ช…๋ชฉ string ํ•˜์œ„ ์œ ํ˜•์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ RegexProvider</^foo$/> ์™€ RegexProvider</^fo{2}$/> ๋Š” ๋ณ„๊ฐœ์˜ ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์œผ๋กœ ์ทจ๊ธ‰๋˜๋Š”๋ฐ, ์ €๋Š” ๊ทธ๋Ÿฐ ๊ฒƒ์„ ์ข‹์•„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋‚ด ์ œ์•ˆ์€ ํ•ต์‹ฌ์— ์žˆ๋Š” ๋ฌธ์ž์—ด๊ณผ ์ง์ ‘ ํ†ตํ•ฉ๋˜๋ฉฐ, ํ˜•์‹ ์–ธ์–ด ์ธ์‹ ์ด๋ก ์˜ ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฐ›์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋งž๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ด‘์‚ฐ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌธ์ž์—ด์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Key extends `on${infer K}` ? K : never ๋˜๋Š” Key extends `${Prefix}${infer Rest}` ? Rest : never ๋ฅผ ํ†ตํ•ด ๋ฌธ์ž์—ด์˜ ์ผ๋ถ€๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ํ˜• ๊ณต๊ธ‰์ž๋Š” ์ด ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€ ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ
  • ๋‚ด ๊ฒƒ์€ ๊ฐœ๋…์  ์ˆ˜์ค€์—์„œ ํ›จ์”ฌ ๋” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด ์—ฐ๊ฒฐ ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์กฐ๊ฑด๋ถ€ ์œ ํ˜•์˜ RHS์— ๋Œ€ํ•ด ์—ญ์„ ์ถ”์ถœํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ •๊ทœ ํ‘œํ˜„์‹ /.*/ ๋Œ€์‹  string ์ž์ฒด์™€ ํ†ตํ•ฉํ•  ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. API ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋‚˜๋จธ์ง€ ์ฝ”๋“œ ๊ธฐ๋ฐ˜๊ณผ ๋Œ€๋ถ€๋ถ„ ๋ถ„๋ฆฌ๋˜๋Š” ์ด๋ก ์ ์œผ๋กœ ๋ณต์žกํ•œ ๋‘ ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ  ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ๋‹ค๋ฅธ ์œ ํ˜•์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ  ๋ฌธ์ž์—ด์—์„œ ์Šฌ๋ผ์ด์Šค๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ฒƒ์€ ๋” ๊ฐ„๋‹จํ•˜์ง€ ์•Š๋”๋ผ๋„ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. , ๊ตฌํ˜„.

BTW, ๋‚ด ์ œ์•ˆ์€ ์—ฌ์ „ํžˆ PhoneNumber ์˜ˆ์ œ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ข€ ๋” ์žฅํ™ฉํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ณณ์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์ด๋ฏธ TS ์˜์—ญ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ๋งํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค(F#์˜ ํ˜•์‹ ๊ณต๊ธ‰์ž๊ฐ€ ๊ฐ€์žฅ ์œ ์šฉํ•œ ์šฉ๋„). (๊ธฐ์ˆ ์ ์œผ๋กœ ์—ฌ๊ธฐ์—์„œ ๊ฐ€๋Šฅํ•œ ์ „ํ™”๋ฒˆํ˜ธ์˜ ์ „์ฒด ๋ชฉ๋ก์œผ๋กœ ํ™•์žฅ๋œ๋‹ค๋Š” ์ ์€ ์ฃผ๋ชฉํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.)

type D = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type PhoneNumber = `${D}${D}${D}-${D}${D}${D}-${D}${D}${D}${D}`;

RegexProvider^foo$/> ๋ฐ RegexProvider^fo{2}$/>๋Š” ๋‘ ๊ฐ€์ง€ ๊ณ ์œ ํ•œ ์œ ํ˜•์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

ํ˜•์‹ ๊ณต๊ธ‰์ž๋Š” ์ผ๋ถ€ equals ๋˜๋Š” compare ๋ฉ”์„œ๋“œ์˜ ๊ตฌํ˜„์„ ์š”๊ตฌํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ •๊ทœ์‹ ํ˜•์‹ ๊ณต๊ธ‰์ž์˜ ํ˜•์‹ ๊ณต๊ธ‰์ž ์ž‘์„ฑ์ž๋Š” ์œ„์˜ ๋‘ ๊ฒฝ์šฐ๊ฐ€ ๋™์ผํ•œ ํ˜•์‹์ž„์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ํ˜• ๊ณต๊ธ‰์ž ์ž‘์„ฑ์ž๋Š” ์›ํ•˜๋Š” ๋Œ€๋กœ ๊ตฌ์กฐ์  ๋˜๋Š” ๋ช…๋ชฉ์  ์œ ํ˜•์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋งˆ๋„ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ์œ ํ˜• ๊ณต๊ธ‰์ž๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ตฌ๋ฌธ์ด ๋™์ผํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹ค์–‘ํ•œ ์ˆ˜์˜ ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์œ ํ˜• ๊ณต๊ธ‰์ž์™€ ๊ฐ€๊นŒ์›Œ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type D = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type PhoneNumber = StringTemplateMatcherProvider<D, D, D, "-", D, D, D, "-", D, D, D, D>;

@AlexLeung ํ•˜์ง€๋งŒ "123-456-7890" ์œ ํ˜•์„ ์œ ํ˜•์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? (๊ทธ๋ ‡๋‹ค๋ฉด ๊ตฌํ˜„์ด ๋ณต์žกํ•ด์ง€๊ณ  ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ๋งŽ์ด ๋Š๋ ค์ง‘๋‹ˆ๋‹ค.)

๋‹น๋ฉดํ•œ ๋…ผ์˜์™€ ๊ด€๋ จ์ด ์žˆ๋Š”๋ฐ, ์œ ํ˜•์ด ๊ณ ์ •๋œ ๊ธธ์ด๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ(์˜ˆ: ์ „ํ™”๋ฒˆํ˜ธ) ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”? ์ตœ๊ทผ์— ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์—ˆ๋˜ ํ•œ ๊ฐ€์ง€ ์ƒํ™ฉ์€ thread_{number} ํ˜•์‹์˜ ๋ฐฉ ์ด๋ฆ„์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š” ์ •๊ทœ์‹์€ thread_[1-9]\d* ์ž…๋‹ˆ๋‹ค. ์ œ์•ˆ๋œ ๋‚ด์šฉ์œผ๋กœ๋Š” ๊ทธ๋Ÿฌํ•œ ํ˜•์‹์„ ์ผ์น˜์‹œํ‚ค๋Š” ๊ฒƒ์ด ์‹คํ˜„ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ์‹ฌ์ง€์–ด ๊ฐ€๋Šฅํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ’์˜ ์ˆซ์ž ๋ถ€๋ถ„์€ ์ด ์ƒํ™ฉ์—์„œ 0๋ณด๋‹ค ํฐ _any_ ๊ธธ์ด๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@jhpratt ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ˆ˜์šฉํ•˜๊ธฐ ์œ„ํ•ด starof ("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9") โ†” /^\d*$/ ํ˜•ํƒœ๋กœ ์ œ์•ˆ์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์•ฝ๊ฐ„๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. string ๊ฐ€ /^[\u0000-\uFFFF]*$/ ๋กœ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ตœ์ ํ™”ํ•˜๋ฏ€๋กœ ์ผ๋ฐ˜ํ™”ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ณ„์‚ฐ ๋ณต์žก์„ฑ ๋ฌธ์ œ๋กœ ์ธํ•ด ์ž„์˜์˜ ๋น„์žฌ๊ท€์  ํ•ฉ์ง‘ํ•ฉ์„ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด starof ๊ทธ ์ด์ƒ์œผ๋กœ ํ™•์žฅํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ์ž„์˜ ์ •๊ทœ์‹*์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ๋‹คํ•ญ์‹ ๊ณต๊ฐ„ ๋˜๋Š” ๋‹คํ•ญ์‹ ์‹œ๊ฐ„ ( ๋‘˜ ๋‹ค ์ตœ์†Œ DFA๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ๋น„๊ตํ•˜์‹ญ์‹œ์˜ค - ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋งค์šฐ ๋Š๋ฆผ). ๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ์‹ค์ œ๋กœ๋Š” a{2} ). ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค(์ง€์ˆ˜ ๋ณต์žก์„ฑ) . ์ด๊ฒƒ์€ ๋™๋“ฑ์„ฑ์„ ์œ„ํ•œ ๊ฒƒ์ด๋ฉฐ, ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ์„ ํ™•์ธํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ •๊ทœ ํ‘œํ˜„์‹์ด ๋‹ค๋ฅธ ์ •๊ทœ ํ‘œํ˜„์‹๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ฌธ์ž์—ด์˜ ํ•˜์œ„ ์ง‘ํ•ฉ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ๋ถ„๋ช…ํžˆ ํ›จ์”ฌ ๋” ๋ณต์žกํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

* ์ˆ˜ํ•™์ ์ธ ์˜๋ฏธ์˜ ์ •๊ทœ์‹: () , (ab) , (a|b) ๋ฐ (a*) , ์—ฌ๊ธฐ์„œ a ๋Š” ๋‹จ์ผ ๋ฌธ์ž๋งŒ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค (a|b) a ๋ฐ b ๋Š” (์ž ์žฌ์ ์œผ๋กœ ๋‹ค๋ฅธ) ์ด ๋ชฉ๋ก์˜ ๊ฐ ๊ตฌ์„ฑ์›์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์•„๋งˆ๋„ ์–ด๋ฆฌ์„์€ ์งˆ๋ฌธ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ... ์ ์ ˆํ•˜๊ฒŒ ์ œํ•œ๋˜์–ด ์žˆ๋‹ค๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ(๋žŒ๋‹ค ๋˜๋Š” ๋ช…๋ช…)์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด ์™œ ์ƒ๋‹นํžˆ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ ์š”์†Œ๊ฐ€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ์ž„์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ":"๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค(์ด์— ๋Œ€ํ•œ ์˜๊ฒฌ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ":"์„ ์›ํ•˜๋Š” ๋Œ€๋กœ ๋Œ€์ฒด).

type email = string : (s) => { return !!s.match(...) }
type phone_number = string : (n) => { return !!String(n).match(...) }
type excel_worksheet_name = string : (s) => { return (s != "History") && s.length <= 31 && ... }

์ดˆ๊ธฐ ์‹œ์ž‘์œผ๋กœ typescript๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ๋งŒ ์ˆ˜๋ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • "๊ธฐ๋ณธ" ์œ ํ˜•์ด์–ด์•ผ ํ•˜๋Š” ํ•„์ˆ˜/๊ฐ€์ •๋œ ๋‹จ์ผ ์ธ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ธฐ ํ•จ์ˆ˜์— ์ •์˜๋œ ์ฐธ์กฐ ๋ณ€์ˆ˜๋งŒ
  • ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(๊ฒ€์ฆ ํ”„๋กœ์„ธ์Šค์—์„œ bool๋กœ ๊ฐ•์ œ ๋ณ€ํ™˜๋จ).

์œ„์˜ ์กฐ๊ฑด์€ typescript ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ™•์ธํ•˜๊ธฐ ์‰ฌ์šด ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉฐ ์ผ๋‹จ ์ด๋Ÿฌํ•œ ์กฐ๊ฑด์ด ๊ฐ€์ •๋˜๋ฉด ๊ตฌํ˜„ ๋ณต์žก์„ฑ์˜ ๋งŽ์€ ๋ถ€๋ถ„์ด ์‚ฌ๋ผ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ์ดˆ๊ธฐ ๋ฒ”์œ„๋ฅผ ๊ด€๋ฆฌ ๊ฐ€๋Šฅํ•œ ํฌ๊ธฐ๋กœ ์ œํ•œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:

  • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์€ ๊ธฐ๋ณธ ์œ ํ˜•(๋ฌธ์ž์—ด, ์ˆซ์ž)์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์—๋งŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋งˆ์ง€๋ง‰ ์ œํ•œ์ด ๊ผญ ํ•„์š”ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ, ๊ทธ๊ฒƒ์ด ํ•„์š”ํ•œ์ง€ ์—ฌ๋ถ€์— ๋Œ€ํ•œ ์งˆ๋ฌธ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ์œ„์˜ ์ œํ•œ์ด ์žˆ๋Š” ์†”๋ฃจ์…˜์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋งŽ์€ ์‹œ๊ฐ„์„ ํ• ์• ํ•  ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ๊ด‘๋ฒ”์œ„ํ•œ ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์œ„์˜ ์ œํ•œ ์‚ฌํ•ญ์„ ๋‚˜์ค‘์— ์™„ํ™”ํ•˜๋ฉด ๊ธฐ๋ณธ ๊ตฌ๋ฌธ์„ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ๋‹จ์ˆœํžˆ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ง€์›ํ•˜๋Š” ์–ธ์–ด์˜ ํญ์„ ํ™•์žฅํ•˜๋Š” ๊ฐ„๋‹จํ•˜๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ํ™•์žฅ์ด ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์œ„์˜ ์ œํ•œ ์‚ฌํ•ญ์˜ ๋‹จ์ ์ด ๊ฑฐ์˜ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@mewalig ๊ทธ๊ฒƒ์€ ๋Ÿฐํƒ€์ž„ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ๊ฒƒ์ด ์‹ค์ œ๋กœ ๋Ÿฐํƒ€์ž„์— ์‹คํ–‰ ๋˜์ง€ ์•Š๊ณ  ์ปดํŒŒ์ผ ์‹œ๊ฐ„์—(๊ทธ๋ฆฌ๊ณ  ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ์„ ํ™•์ธํ•˜๋ ค๋Š” ๋•Œ๋งˆ๋‹ค) ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜ ๋Š” ๋Ÿฐํƒ€์ž„ (๋ณ€์ˆ˜, ํ•จ์ˆ˜)

๊ฒŒ๋‹ค๊ฐ€ ๋‹น์‹ ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋‹น์‹ ์ด ๋˜์ง€๋Š” ์–ด๋–ค ๊ฒƒ, ํŠนํžˆ ์ž˜๋ชป ์ตœ์ ํ™”๋œ ํ•จ์ˆ˜๋‚˜ ์™„์ „ํžˆ ์•…์˜์ ์ธ while(true){} ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ํ˜„๋ช…ํ•˜๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„ ์ฝ”๋“œ๊ฐ€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์‹คํ–‰๋˜๋„๋ก ๋ฌด์ž‘์œ„๋กœ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์ด "PHP ๋ฐฉ์‹"์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์ œ์•ˆํ•œ ๊ตฌ๋ฌธ์€ ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์„ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

let runtime: types = runtime;

(์ฆ‰, ๊ฒฐ์žฅ ๋’ค์˜ ์œ ํ˜•) ๋’ค์ง‘์–ด์„œ, ํšจ๊ณผ์ ์œผ๋กœ

type types = types: runtime;

๋”์ฐํ•ฉ๋‹ˆ๋‹ค. ์ œ์•ˆํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๊ฒƒ์€ ํ™•์‹คํžˆ ๋‚˜์œ ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜ ๋Š” ๋Ÿฐํƒ€์ž„ (๋ณ€์ˆ˜, ํ•จ์ˆ˜)

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€์žˆ๋Š” ๊ฒฝ์šฐ ๋ฌผ๋ก  ๊ทธ๋“ค์€, ECMA ์Šคํฌ๋ฆฝํŠธ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ๋Ÿฐํƒ€์ž„ ์ˆ˜ ( tsc BTW, ์•Š์Šต๋‹ˆ๋‹ค!). fetch() ์™€ ๊ฐ™์€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ์˜๋ฏธ์™€ ๋Ÿฐํƒ€์ž„ ์˜๋ฏธ์˜ ๋ชจํ˜ธ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ถ„๋ช…ํžˆ ์žˆ์ง€๋งŒ ์ด๊ฒƒ์ด ๋ฐ”๋กœ ๋ฐ˜๋ณต์— ๊ด€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋Ÿฐํƒ€์ž„ ์ฝ”๋“œ๊ฐ€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์‹คํ–‰๋˜๋„๋ก ๋ฌด์ž‘์œ„๋กœ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ์ด "PHP ๋ฐฉ์‹"์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

C++ constexpr functions ์™€ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•ด๊ฒฐ์ฑ…์€ constexpr ๋Š” constexpr ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ชจ๋“  ๊ฒƒ์ด constexpr ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒ๋‹นํžˆ ๊ฐ•๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„ ํŒŒ์ผ ์‹œ์Šคํ…œ์— ๋Œ€ํ•ด constexpr -๋™๋“ฑํ•œ ๋ฒ„์ „์˜ ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ๋„ ๋Œ€๋žต ๊ดœ์ฐฎ์•„ ๋ณด์ž…๋‹ˆ๋‹ค. LHS๋Š” ์œ ํ˜•์ด๊ณ  ๋ฌผ๋ก  RHS๋„ ์ผ์ข…์˜ ์œ ํ˜•์ž…๋‹ˆ๋‹ค. ๋‚ด ๋ฌธ์ œ๋Š” "๊ธฐ๋ณธ" ์œ ํ˜•์„ ๋„˜์–ด ์œ ํ˜•์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๊ด€ํ•œ ๊ฒƒ์ด์ง€๋งŒ ๊ทธ๊ฒƒ๋„ ๋ชจ๋‘ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ์•ˆํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๊ฒƒ์€ ํ™•์‹คํžˆ ๋‚˜์œ ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๊ฒฐ๊ตญ ๋‚˜์œ ์ƒ๊ฐ์ด ๋  ์ˆ˜ ์žˆ์ง€๋งŒ ์ง€๊ธˆ์€ ํƒ€์ดํ”„์Šคํฌ๋ฆฝํŠธ์˜ ๋ชฉํ‘œ์—์„œ ๋„ˆ๋ฌด ๋ฉ€๋ฆฌ ๋ฒ—์–ด๋‚˜์•ผ ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๋งค์šฐ ๋œ ๊ตฌ์ฒดํ™”๋œ ์•„์ด๋””์–ด๋ฅผ ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๊ณผ ์œ ์‚ฌํ•œ ์ข‹์€ ์•„์ด๋””์–ด๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ์˜๋ฏธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค!

์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋…ผ์˜๋Š” ์ง€๊ธˆ์€ ์ค‘๋‹จ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค( PR ์€ ์ข…๋ฃŒ๋˜์—ˆ์œผ๋ฉฐ ๋””์ž์ธ ๋…ธํŠธ ํŒ€์— ๋”ฐ๋ฅด๋ฉด _๋ช…๋ชฉ ์œ ํ˜•๊ณผ ์ผ๋ฐ˜ํ™”๋œ ์ธ๋ฑ์Šค ์„œ๋ช…์ด ์žˆ์„ ๋•Œ๊นŒ์ง€ ์—ฌ๊ธฐ์— ์ปค๋ฐ‹ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ๋“ค์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค._).

์–ด์จŒ๋“ , ์ •๊ทœ์‹ ํŒจํ„ด ์ถ”์ถœ์„ ์ง€์›ํ•˜๋Š” ํ˜„์žฌ PR์— ๋Œ€ํ•œ ๋˜ ๋‹ค๋ฅธ ๊ฐ€์ƒ ํ™•์žฅ์„ ์ œ์•ˆํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค( @isiahmeadows ๊ฐ€ ์ž์‹ ์˜ ์ œ์•ˆ์„ ์ œ์‹œํ–ˆ์ง€๋งŒ ์†”์งํžˆ ์ง€๊ธˆ์€ ๋จธ๋ฆฌ๋ฅผ ๊ฐ์Œ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค...).

๋‚˜๋Š” ํ˜„์žฌ PR์„ ์ •๋ง ์ข‹์•„ํ•˜๊ณ  ๊ทธ๊ฒƒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‚ด ์ œ์•ˆ์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•จ์ˆ˜(๋ฐ infer ํ‚ค์›Œ๋“œ๊ฐ€ ์žˆ๋Š” ์กฐ๊ฑด๋ถ€ ์œ ํ˜•)์— ๋Œ€ํ•ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ œ๋„ค๋ฆญ ์œ ํ˜• ์ธ์ˆ˜ ์ถ”๋ก ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ๋ฌธ์„ ์ œ์•ˆํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์€ ์ด๋ฏธ ์ผ๋ฐ˜ ํ•จ์ˆ˜์—์„œ ์ „๋‹ฌ๋œ ๋ฆฌํ„ฐ๋Ÿด ๊ฐœ์ฒด์—์„œ ์œ ํ˜•์„ "์ถ”์ถœ"ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ง๊ด€์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์ด ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

type Prop1 = /(\w)\.(\w)/

์ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const goodLiteral = "foo.bar";
const badLiteral = "foo";
const regextTest: Prop1 = goodLiteral; //no error
const regextTest: Prop1 = badLiteral; //compiler error

function funProp1(prop: Prop1) { } 

funProp1(goodLiteral); //no error
funProp1(badLiteral); //error

๊ทธ๋Ÿฌ๋‚˜ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜์—์„œ Regex ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ๋•Œ ๊บพ์‡  ๊ด„ํ˜ธ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ผ์น˜ํ•˜๋Š” ๋ฌธ์ž์—ด์„ ์ถ”๋ก ํ•˜๋ ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด

type Prop1 = /(\w)\.(\w)/
const Prop1 = /(\w)\.(\w)/

const goodLiteral = "foo.bar";
const badLiteral = "foo";

function funProp1<M1 extends string, M2 extends string>(prop: Prop1<M1, M2>) : [M1, M2] 
{
    const m = prop.match(Prop1);
    return [m[1], m[2]];
} 

const res1 = funProp1(goodLiteral); //no error. Function signature inferred to be funProp<"foo", "bar">(prop: Prop1<"foo", "bar">) : ["foo", "bar"]
const res2 = funProp1(badLiteral); //compiler error

res1 ์˜ ์œ ์ถ”๋œ ์œ ํ˜•์€ ["foo", "bar"]

์œ ์šฉํ•œ๊ฐ€์š”?

  1. Ember.js/lodash ๊ฐ€์ ธ์˜ค๊ธฐ ๊ธฐ๋Šฅ

๋‹ค์Œ ์ฝ”๋“œ๊ฐ€ ์ž‘๋™ํ•˜๋„๋ก ์œ ํ˜• ์•ˆ์ „ "๋ฌธ์ž์—ด ๊ฒฝ๋กœ" getter๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const deep = get(objNested, "nested.very.deep")

๊ทธ๋Ÿฌ๋‚˜ ๊ฐ€๋Šฅํ•œ get ์˜ "๊นŠ์ด"์˜ ๊ณ ์ •๋œ ์ตœ๋Œ€ ์ˆ˜์— ๋Œ€ํ•ด ๋งŽ์€ ๊ณผ๋ถ€ํ•˜๋ฅผ ํ”ผํ•˜๋ ค๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  1. ๋งคํ•‘๋œ ์œ ํ˜•์—์„œ ์ถ”์ถœ๋œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด https://github.com/Microsoft/TypeScript/issues/12754 ์™€ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด { [ StripAsyncSuffix<P> for P in K ] : T[P] } ์™€ ๊ฐ™์€ ๊ตฌ๋ฌธ, ๋ˆ„๊ตฐ๊ฐ€ ์ด๋ฏธ ์ด์™€ ์œ ์‚ฌํ•œ ๊ฒƒ์„ ์ œ์•ˆ ํ•จ)

์•„๋งˆ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๋„ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์€ ์ด ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์— ์ ํ•ฉํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค(1. ์ œ๊ณต๋œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ์ ˆํ•œ ์œ ํ˜• ํŒŒ์•…, 2. ์ž…๋ ฅ ์œ ํ˜•์˜ ์†์„ฑ ์ด๋ฆ„์„ ์ƒˆ๋กœ ์ •์˜๋œ ์œ ํ˜•์˜ ์ƒˆ ์†์„ฑ ์ด๋ฆ„์œผ๋กœ ๋ณ€ํ™˜)

์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ž…๋‹ˆ๋‹ค.

์ €๋Š” ํ˜„์žฌ URL์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งž์ถค ๋ฆฐํŠธ ๊ทœ์น™์„ ๋งŒ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ํ›จ์”ฌ ๋” ์‰ฌ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ์ด๊ฒƒ์€ ์ฝ”๋“œ ๊ธฐ๋ฐ˜ ์ „์ฒด์—์„œ props์˜ ์œ ํšจ์„ฑ์„ ์ฃผ์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํ›จ์”ฌ ๋” ๋งŽ์€ ๊ถŒํ•œ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์œ ํ˜• ๊ณต๊ธ‰์ž, ํ…œํ”Œ๋ฆฟ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ๋˜๋Š” ๊ธฐํƒ€ ์ œ์•ˆ์— ๋Œ€ํ•œ ์ด๋™์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ด๊ฒƒ์€ ํ›Œ๋ฅญํ•œ ๋„๊ตฌ๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ๋‚˜์˜ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ํ˜„์žฌ์™€ ๊ฐ™์€ ๋งˆ์ปค ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค ์ด .

interface TickerSymbol extends String {}

์œ ์ผํ•œ ๋ฌธ์ œ๋Š” ์ธ๋ฑ์Šค ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๊ฒฝ์šฐ string ๋กœ ์บ์ŠคํŒ…ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

interface TickerSymbol extends String {}
var symbol: TickerSymbol = 'MSFT';
// declare var tickers: {[symbol: TickerSymbol]: any}; // Error: index key must be string or number
declare var tickers: {[symbol: string]: any};
// tickers[symbol]; // Type 'TickerSymbol' cannot be used as an index type
tickers[symbol as string]; // OK

๊ทธ๋Ÿฌ๋‚˜ JavaScript๋Š” String (๋Œ€๋ฌธ์ž S ํฌํ•จ)์˜ ์ธ๋ฑ์Šค ์œ ํ˜•์—์„œ ๊ดœ์ฐฎ์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

var obj = { one: 1 }
var key = new String('one');
obj[key]; // TypeScript Error: Type 'String' cannot be used as an index type.
// but JS gives expected output:
// 1

@DanielRosenwasser ์—ฌ๊ธฐ ์— ์ œ์•ˆ ์ด ์žˆ๊ณ 

์œ„์˜ ์ œ์•ˆ์„ ๊ฒ€ํ† ํ–ˆ์œผ๋ฉฐ ๋ช‡ ๊ฐ€์ง€ ์งˆ๋ฌธ๊ณผ ์˜๊ฒฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ์ œ์•ˆ์„œ์˜ ๋ฌธ์ œ์ 

๋ฐฉ์ถœ์„ ์ƒ์„ฑํ•˜๋Š” ์œ ํ˜•

์šฐ๋ฆฌ๋Š” ์œ ํ˜• ์‹œ์Šคํ…œ์„ ์™„์ „ํžˆ ์ง€์šฐ๊ธฐ ์œ„ํ•ด ์ตœ์„ ์„ ๋‹คํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ฐฉ์ถœ๋œ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์œ ํ˜• ๋ณ„์นญ์ด ํ•„์š”ํ•œ ์ œ์•ˆ์€ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚ฉ๋‹ˆ๋‹ค. ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋ช…ํ™•ํ•˜์ง€ ์•Š์€ ๋ฐฉ์‹์œผ๋กœ ๋ฐœ์ƒํ•œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ๊ฐ•์กฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -220180091 - ํ•จ์ˆ˜์™€ ์œ ํ˜•์„ ๋™์‹œ์— ์ƒ์„ฑ

type Integer(n:number) => String(n).macth(/^[0-9]+$/)

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -261519733 - ์ด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

type CssColor = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
// ... later
setFontColorFromString(color: string) {
    fontColor = color;// compile time error
    if (CssColor.test(color)) {
    //  ^^^^^^^^ no value declaration of 'CssColor' !
        fontColor = color;// correct
    }
}

๋‹ค์‹œ ๋ง์”€๋“œ๋ฆฌ์ง€๋งŒ ์ด๊ฒƒ์€ ์ดˆ๋ณด์ž์šฉ ์ž…๋‹ˆ๋‹ค. TypeScript์˜ ์œ ํ˜•์€ ๊ตฌ์„ฑ ๊ฐ€๋Šฅ ํ•˜๋ฉฐ ์œ ํ˜•์—์„œ JS๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์€ ์ด ์„ธ๊ณ„์—์„œ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๊นŒ์ง€ ๊ฐ€์žฅ ๊ธด ์ œ์•ˆ ์—๋Š” ๊ด‘๋ฒ”์œ„ํ•œ ๋ฐฉ์ถœ ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ด‘๋ฒ”์œ„ํ•œ ์œ ํ˜• ์ง€์ • ๋ฐฉ์ถœ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

type Matcher<T extends number | boolean> = T extends number ? /\d+/ : /true|false/;
function fn<T extends number | boolean(arg: T, s: Matcher<T>) {
  type R = Matcher<T>
  if (R.test(arg)) {
      // ...
  }
}
fn(10, "10");
fn(false, "false");

๊ต์ฐจ๋กœ ๊ธˆ์ง€

์‹ค์ œ๋กœ ์ผ๋ฐ˜์ ์ธ ์œ ํ˜•๊ณผ ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆ๋œ ์œ ํ˜•์€ ์‹ค์ œ๋กœ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ•ฉ์ง‘ํ•ฉ๊ณผ ๊ต์ฐจ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๊ทœ์น™์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

type Regex_1 = / ... /;
type Regex_2 = / ... /;
type NonRegex = { ... };
type test_4 = Regex_1 & NonRegex;// compile time error

TypeScript๋Š” ๊ต์ฐจ์˜ ์ธ์Šคํ„ด์Šคํ™”์—์„œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ด๊ฒƒ์€ ์ตœ์ข… ๋””์ž์ธ์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

์ธ๊ฐ„ ๊ณตํ•™

์ „๋ฐ˜์ ์œผ๋กœ ๊ฐ€์žฅ ๋‘๋“œ๋Ÿฌ์ง„ ์‹œ์‚ฌ์ ์€ ๋™์ผํ•œ RegExp๋ฅผ ๋‘ ๋ฒˆ (๊ฐ’ ๊ณต๊ฐ„์—์„œ ํ•œ ๋ฒˆ, ์œ ํ˜• ๊ณต๊ฐ„์—์„œ ํ•œ

์œ ํ˜• ๋ฐฉ์ถœ์— ๋Œ€ํ•œ ์œ„์˜ ์šฐ๋ ค๋ฅผ ๊ฐ์•ˆํ•  ๋•Œ ๊ฐ€์žฅ ํ˜„์‹ค์ ์ธ ์†”๋ฃจ์…˜์€ ๊ฐ’ ๊ณต๊ฐ„์— ํ‘œํ˜„์‹์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// Probably put this in lib.d.ts
type PatternOf<T extends RegExp> = T extends { test(s: unknown): s is infer P } ? P : never;

const ZipCode = /^\d\d\d\d\d$/;
function map(z: PatternOf<typeof ZipCode>) {
}

map('98052'); // OK
map('Redmond'); // Error

๋ฌผ๋ก  ์—ฌ์ „ํžˆ RegExp๋ฅผ ํ˜•์‹ ๊ณต๊ฐ„์— ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋Ÿฐํƒ€์ž„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ์‚ฌ์šฉ์—๋Š” ๋‹ค์‹œ ํ…Œ์ŠคํŠธ ๋˜๋Š” ์–ด์„ค์…˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

function map(z: /^\d\d\d\d\d$/) { }
map('98052'); // OK
map('Redmond'); // Error

function fn(s: string) {
    map(s); // Error
    // typo
    if (/^\d\d\d\d$/.test(s)) {
        // Error, /^\d\d\d\d$/ is not assignable to /^\d\d\d\d\d$/
        map(s);
    }

    if (/^\d\d\d\d\d$/.test(s)) {
        // OK
        map(s);
    }
}

์‚ฌ์šฉ ์‚ฌ๋ก€ ์ˆ˜์ง‘ ๋ฐ ์„ค๋ช…

์ƒˆ๋กœ์šด ์œ ํ˜•์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์ด์ƒ์ ์œผ๋กœ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

  • ํ•ด๊ฒฐ๋˜๊ณ  ์žˆ๋Š” ๋ฌธ์ œ์—๋Š” ๋” ๋‚˜์€ ๋Œ€์•ˆ ์ด ์—†์Šต๋‹ˆ๋‹ค(์•„์ง ์–ธ์–ด์— ์—†๋Š” ๊ทธ๋Ÿด๋“ฏํ•œ ๋Œ€์•ˆ ํฌํ•จ).
  • ์‹ค์ œ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์˜๋ฏธ ์žˆ๋Š” ๋นˆ๋„ ๋กœ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ์ œ์•ˆ๋œ ์†”๋ฃจ์…˜์€ ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ์ž˜ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌํ„ฐ๋Ÿด์˜ ์ปดํŒŒ์ผ ํƒ€์ž„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

์ด ์Šค๋ ˆ๋“œ๋Š” ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์ธ ์˜ˆ๋Š” ๋” ๋“œ๋ฌผ์—ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์ด๋Ÿฌํ•œ ์˜ˆ ์ค‘ ๋งŽ์€ ๋ถ€๋ถ„์ด ์™„์ „ ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ํšจํ•œ ์ž…๋ ฅ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” RegExp๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ๊ธ€๊ผด ์ƒ‰์ƒ - 16์ง„์ˆ˜ ์ƒ‰์ƒ์„ ํ—ˆ์šฉํ•˜๋Š” AFAIK๋Š” "ํฐ์ƒ‰" ๋˜๋Š” "ํ•˜๋Š˜์ƒ‰"๋„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋˜ํ•œ rgb(255, 0, 0) ๊ตฌ๋ฌธ์„ ์ž˜๋ชป ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ, ์šฐํŽธ๋ฒˆํ˜ธ ๋“ฑ - ์ข‹์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ฝ”๋“œ์— ๋ฌธ์ž ๊ทธ๋Œ€๋กœ์˜ ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ ๋˜๋Š” ์šฐํŽธ๋ฒˆํ˜ธ๊ฐ€ ์žˆ๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ ๋ช…๋ชฉ ์œ ํ˜•์ด ํ•„์š”ํ•ฉ๋‹ˆ๊นŒ? RegExp๋กœ ์ •ํ™•ํ•˜๊ฒŒ ์„ค๋ช…ํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ž์—ด์˜ ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ์žˆ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ? "๊ฒฝ์Ÿ ์ œ์•ˆ" ์ฐธ์กฐ

    • ์ •์ˆ˜ - "3e5" ์ž˜๋ชป ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

    • ์ด๋ฉ”์ผ - ์ด๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋‚˜์œ ์ƒ๊ฐ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค . ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ ์ฝ”๋“œ์— ์ด๋ฉ”์ผ ์ฃผ์†Œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด

    • CSS ํ…Œ๋‘๋ฆฌ ์‚ฌ์–‘ - ๋…๋ฆฝ ์‹คํ–‰ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ž์ฒด ์ง€์›ํ•˜๋Š” DSL์„ ์„ค๋ช…ํ•˜๋Š” ์ •ํ™•ํ•œ RegEx๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ฏฟ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    • ํ…Œ์ŠคํŠธ ์ž‘์„ฑ - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ๋งŽ์ด ์ œ๊ณตํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ๊ฑฐ์˜ ๋Œ€์œ„๋ฒ•์ด์ง€๋งŒ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ž…๋ ฅ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.

    • ๋‚ ์งœ ํ˜•์‹ - ์–ด๋–ป๊ฒŒ/์™œ? Date ์—๋Š” ์ด๋ฅผ ์œ„ํ•œ ์ƒ์„ฑ์ž๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž…๋ ฅ์ด ๋Ÿฐํƒ€์ž„ ์™ธ๋ถ€์—์„œ ์˜ค๋Š” ๊ฒฝ์šฐ ๋ช…๋ชฉ ์œ ํ˜•์„ ์›ํ•ฉ๋‹ˆ๋‹ค.

    • URI - ๋‹น์‹ ์ด ์ƒ์ƒํ•  ์ˆ˜์žˆ๋Š” fetch ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค host ๋กœ๋˜์ง€ ์•Š๋Š”์— http(s?):

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

ํ•œ ๊ฐ€์ง€ ์šฐ๋ ค ์‚ฌํ•ญ์€ "์ •๋ฐ€๋„์—ผ"์ž…๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ํ™•์‹คํžˆ Typed์— ์œ ์šฉํ•˜๊ฒŒ ํ‘œ์‹œ๋˜๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ชจ๋“  ํ•จ์ˆ˜์— RegExp ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋“  ๋น„๋ฌธ์ž์  ํ˜ธ์ถœ์ด ์ค‘๋‹จ๋˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ? ์„ค์ƒ๊ฐ€์ƒ์œผ๋กœ ์ •์˜ ํŒŒ์ผ ์ž‘์„ฑ์ž๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ RegExp์˜ "์˜ฌ๋ฐ”๋ฅธ ์ฒ ์ž"๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์†Œ๋น„์ž์™€ ์ •ํ™•ํžˆ ๋™์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ URL๋กœ ์ž๊ฒฉ์ด ์žˆ๋Š” ๊ฒƒ, ํ˜ธ์ŠคํŠธ ์ด๋ฆ„์œผ๋กœ ์ž๊ฒฉ์ด ์žˆ๋Š” ๊ฒƒ, ์ด๋ฉ”์ผ๋กœ ์ž๊ฒฉ์ด ์žˆ๋Š” ๊ฒƒ ๋“ฑ์— ๋Œ€ํ•œ ์ž์ฒด ๋ฒ„์ „์ด ์žˆ๊ณ  ๋‘ ๊ฐœ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์‚ฌ๋žŒ์ด ์žˆ๋Š” ๋ฐ”๋ฒจํƒ‘ ์ƒํ™ฉ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์šฐ๋ฆฌ๋ฅผ ์•ˆ๋‚ดํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋งŒ์กฑ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ํ˜•์‹ ์ฃผ์žฅ์„ ์‚ฝ์ž…ํ•˜๊ฑฐ๋‚˜ ์ •๊ทœ ํ‘œํ˜„์‹์„ ๋ณต์‚ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋Ÿฐํƒ€์ž„ ๊ฒ€์‚ฌ ์‹œํ–‰

์ด์ „ ์ธ์ฒด ๊ณตํ•™ ์„น์…˜์˜ fn ์™€ ๊ฐ™์ด ํ•จ์ˆ˜์˜ ์ธ์ˆ˜๊ฐ€ ์ด์ „ ์ •๊ทœ์‹์— ์˜ํ•ด ๊ฒ€์ฆ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋Š” ๊ฒ€์‚ฌ์— ๋Œ€ํ•œ ๋…ผ์˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•œ RegEx๊ฐ€ ์ž˜ ์•Œ๋ ค์ง„ ๊ฒฝ์šฐ ์ด๋Š” ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ ํฐ "๋งŒ์•ฝ"์ž…๋‹ˆ๋‹ค. ์ œ ๊ธฐ์–ต์œผ๋กœ๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ •๊ทœ์‹์„ ์ œ๊ณตํ•˜๋Š” ๋‹จ์ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ธฐ์–ต๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋Š” ์ œ๊ณตํ• 

์ด ํ‰๊ฐ€์— ๋Œ€ํ•œ ๋ฐ˜๋Œ€ ์ฆ๊ฑฐ๋Š” ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์†์„ฑ ํ‚ค / ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์ธ๋ฑ์„œ

์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์†์„ฑ ์ด๋ฆ„์— ๋”ฐ๋ผ ๊ฐœ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด React์—์„œ ์ด๋ฆ„์ด aria- ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์†Œํ’ˆ์— ์œ ํ˜•์„ ์ ์šฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

์ด๊ฒƒ์€ ์‚ฌ์‹ค์ƒ ์ง๊ต ๊ฐœ๋…์ž…๋‹ˆ๋‹ค(Regex ์†์„ฑ ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  Regex ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ทธ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค).

TODO(๋‚˜ ๋˜๋Š” ๋ˆ„๊ตฌ๋‚˜): ์ด์— ๋Œ€ํ•œ ๋ณ„๋„์˜ ๋ฌธ์ œ๋ฅผ ์—ฝ๋‹ˆ๋‹ค.

๊ฒฝ์Ÿ ์ œ์•ˆ

๋ช…๋ชฉ ๋˜๋Š” ํƒœ๊ทธ ์œ ํ˜•

์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ช…๋ชฉ/ํƒœ๊ทธ ์œ ํ˜•์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

type ZipCode = make_unique_type string;

๊ทธ๋Ÿฐ ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function asZipCode(s: string): ZipCode | undefined {
    return /^\d\d\d\d\d$/.test(s) ? (s as ZipCode) : undefined;
}

์ด ์‹œ์ ์—์„œ ์ •๋ง RegExp ์œ ํ˜•์ด ํ•„์š”ํ• ๊นŒ์š”? ์ž์„ธํ•œ ๋‚ด์šฉ์€ "์ปดํŒŒ์ผ ์‹œ๊ฐ„" ํ™•์ธ ์„น์…˜์„ ์ฐธ์กฐํ•˜์„ธ์š”.

๋ฐ˜๋Œ€๋กœ, ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋ช…๋ชฉ์ƒ์˜ ์œ ํ˜•์„ ์ •๊ทœ์‹ ์œ ํ˜•์„ ๊ฐ€์ง€๊ณ ํ•˜์ง€ ๊ฐ€์ • ํ•ด ๋ด…์‹œ๋‹ค. ๊ฒ€์ฆ๋˜์ง€ ์•Š์€ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘(ab)ํ•˜๋Š” ๊ฒƒ์€ ๊ฝค ์œ ํ˜น์ ์ž…๋‹ˆ๋‹ค .

type Password = /(IsPassword)?.*/;
type UnescapedString = /(Unescaped)?.*/;
declare function hash(p: Password): string;

const p: Password = "My security is g00d"; // OK
const e: UnescapedString = "<div>''</div>"; // OK
hash(p); // OK
hash(e); // Error
hash("correct horse battery staple"); // OK

์Šค๋ ˆ๋“œ์—์„œ ์ผ๋ฐ˜์ ์ธ ๊ฒƒ์€ ์ด๋Ÿฌํ•œ ์ •๊ทœ์‹์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์ฝ”๋“œ๊ฐ€ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹Œ ๋Ÿฐํƒ€์ž„ ์ œ๊ณต ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋”๋ผ๋„ ํ…Œ์ŠคํŠธ ๋ฌธ์ž์—ด์ด " ์˜ณ์€". ์ด๊ฒƒ์€ ๋Œ€์‹ ์— ๋ช…๋ชฉ/ํƒœ๊ทธ/๋ธŒ๋žœ๋“œ ๋ฌธ์ž์—ด์— ๋Œ€ํ•œ ์ธ์ˆ˜๋กœ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์„ ์–ด๋Š ์ชฝ์ด๋“  ์ž‘์„ฑํ•˜๊ณ  ํ…Œ์ŠคํŠธ์˜ ์ด์ ์€ ์ฒ ์ €ํ•˜๊ฒŒ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ ์ž…๋ ฅ์˜ ๋ชจ๋“  ์˜ค๋ฅ˜๋Š” ๊ฐœ๋ฐœ ์ฃผ๊ธฐ ์ดˆ๊ธฐ์— ํ”Œ๋ž˜๊ทธ ์ง€์ •).

๋น„์ด์Šˆ

์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ธก๋ฉด์— ๋Œ€ํ•ด ๋…ผ์˜ํ–ˆ์œผ๋ฉฐ ์ฐจ๋‹จ ์š”์†Œ๊ฐ€ ์•„๋‹Œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค.

ํ˜ธ์ŠคํŠธ ๊ธฐ๋Šฅ

์ตœ์‹  ๋Ÿฐํƒ€์ž„์€ ์ด์ „ ๋Ÿฐํƒ€์ž„๋ณด๋‹ค ๋” ๋งŽ์€ RegExp ๊ตฌ๋ฌธ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. TypeScript ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์œ„์น˜์— ๋”ฐ๋ผ ์ตœ์‹  RegExp ๊ธฐ๋Šฅ์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๋Š” ๋Ÿฐํƒ€์ž„ ๊ธฐ๋Šฅ์— ๋”ฐ๋ผ ํŠน์ • ์ฝ”๋“œ๊ฐ€ ์œ ํšจํ•˜๊ฑฐ๋‚˜ ์œ ํšจํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ, ๋Œ€๋ถ€๋ถ„์˜ ์ƒˆ๋กœ์šด RegExp ๊ธฐ๋Šฅ์€ ์ƒ๋‹นํžˆ ๋‚œํ•ดํ•˜๊ฑฐ๋‚˜ ๊ทธ๋ฃน ์ผ์น˜์™€ ๊ด€๋ จ๋˜์–ด ์—ฌ๊ธฐ์—์„œ ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์„ฑ๋Šฅ

RegExes๋Š” ๋ฌดํ•œํ•œ ์–‘์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํฐ ๋ฌธ์ž์—ด์— ๋Œ€ํ•œ ์ผ์น˜๋Š” ์ž„์˜๋กœ ๋งŽ์€ ์–‘์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ด๋ฏธ ๋‹ค๋ฅธ ์ˆ˜๋‹จ์„ ํ†ตํ•ด ์Šค์Šค๋กœ DOS๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์•…์˜์ ์œผ๋กœ ๋น„ํšจ์œจ์ ์ธ RegExp๋ฅผ ์ž‘์„ฑํ•  ๊ฐ€๋Šฅ์„ฑ์€ ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค.

ํ•˜์œ„ ์œ ํ˜•( /\d*/ -> /.*/ ?), ์กฐํ•ฉ, ๊ต์ฐจ๋กœ ๋ฐ ๊ฑฐ์ฃผ ๋ถˆ๊ฐ€๋Šฅ

์ด๋ก ์ƒ /\d+/ ๋Š” /.+/ ์˜ ์•Œ ์ˆ˜ ์žˆ๋Š” ํ•˜์œ„ ์œ ํ˜•์ž…๋‹ˆ๋‹ค. ํ•œ RegExp๊ฐ€ ๋‹ค๋ฅธ RegExp์˜ ์ˆœ์ˆ˜ํ•œ ํ•˜์œ„ ์ง‘ํ•ฉ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€(ํŠน์ • ์ œ์•ฝ ์กฐ๊ฑด์—์„œ) ๊ฒฐ์ •ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์กด์žฌํ•˜์ง€๋งŒ ๋ถ„๋ช…ํžˆ ํ‘œํ˜„์‹์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๋Š” RegExpes๊ฐ€ ์ผ์น˜ํ•˜๋Š” ํ•ญ๋ชฉ์— ๋”ฐ๋ผ ์•”์‹œ์  ํ•˜์œ„ ์œ ํ˜• ๊ด€๊ณ„๋ฅผ ํ˜•์„ฑํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์— ๋Œ€ํ•ด 100% ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์•„๋งˆ๋„ ๋” ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ ๊ด€๊ณ„๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ •์˜๋˜์–ด ์žˆ๋Š” ํ•œ Union ๋ฐ Intersection ์ž‘์—…์€ "์ฆ‰์‹œ" ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

TypeScript์—์„œ ๋‘ ๊ฐœ์˜ ๊ธฐ๋ณธ ์œ ํ˜•์ด ๊ต์ฐจ์ ์—์„œ "์ถฉ๋Œ"ํ•˜๋ฉด never ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ RegExp๊ฐ€ ๊ต์ฐจํ•  ๋•Œ ๋‘ ํ‘œํ˜„์‹์˜ ๊ต์ฐจ์™€ ์ผ์น˜ํ•˜๋Š” ์ƒˆ RegExp๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋Œ€์‹  /a/ & /b/ ๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. never ๋กœ์˜ ๊ฐ์†Œ๋Š” ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์–ด๋–ค ๋ฌธ์ž์—ด๋„ ์–‘์ชฝ์„ ๋งŒ์กฑ์‹œํ‚ฌ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(์ด๊ฒƒ์€ ์•ž์—์„œ ์„ค๋ช…ํ•œ re: subtyping๊ณผ ์œ ์‚ฌํ•œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค).

๋‹ค์Œ ๋‹จ๊ณ„

์š”์•ฝํ•˜๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์ •๊ทœ์‹ ์ด๋ฆ„ ์†์„ฑ ํ‚ค(์ผ๋ช… ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์ธ๋ฑ์„œ)์— ๋Œ€ํ•œ ๋ณ„๋„ ๋ฌธ์ œ ์ œ์ถœ
  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ์ปดํŒŒ์ผ ํƒ€์ž„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ๊ตฌ์ฒด์ ์ด๊ณ  ๊ทธ๋Ÿด๋“ฏํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์–ป๊ธฐ

    • ์˜ˆ: ์ด๋กœ๋ถ€ํ„ฐ ํฐ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ํ™•์‹คํžˆ ์œ ํ˜• ๋˜๋Š” ๊ธฐํƒ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ํ•จ์ˆ˜ ์‹๋ณ„

  • ๋ช…๋ชฉ/ํƒœ๊ทธ/๋ธŒ๋žœ๋“œ ์œ ํ˜•์ด ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ๋Œ€ํ•ด ๋ณด๋‹ค ์œ ์—ฐํ•˜๊ณ  ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅํ•œ ์†”๋ฃจ์…˜์ธ์ง€ ์ดํ•ด
  • ์ด๋ฏธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ RegEx๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‹๋ณ„

์‚ฌ์šฉ ์‚ฌ๋ก€: ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ(https://github.com/hyperhype/hyperscript)์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ
ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ ํ•จ์ˆ˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ h('div#some-id') ์™€ ๊ฐ™์ด ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
regex-ish ํŒจํ„ด ๋งค์ฒ˜๋Š” h ์˜ ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ฉฐ ์ด๋Š” ์˜ˆ์ œ์˜ ๊ฒฝ์šฐ HTMLDivElement๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์œ ํ˜• ์‹œ์Šคํ…œ์ด ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  CSS ์†์„ฑ์ด ์œ ํ˜• ์•ˆ์ „ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

declare let width: number;
declare let element: HTMLElement;

element.style.height = `${width}px`;
// ...or
element.style.height = `${width}%`;

CSS ์„ ํƒ๊ธฐ์˜ ์œ ํšจ์„ฑ๋„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค( element.class#id - ์œ ํšจ, div#.name - ์œ ํšจํ•˜์ง€ ์•Š์Œ)

์บก์ฒ˜ ๊ทธ๋ฃน์ด (์–ด์จŒ๋“ ) ์ž‘๋™ํ•œ๋‹ค๋ฉด Lodash์˜ get ๋ฉ”์†Œ๋“œ๋Š” ์œ ํ˜• ์•ˆ์ „ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');

์ด๊ฒƒ๋„ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface IOnEvents {
  [key: PatternOf</on[a-z]+/>]: (event: Event) => void;
}

interface IObservablesEndsOn$ {
  [key: PatternOf</\$$/>]: Observable<any>;
}

์‚ฌ์šฉ ์‚ฌ๋ก€: ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ(ํ•˜์ดํผํ•˜์ดํ”„/ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ) ์œ ์‚ฌ ํ•จ์ˆ˜

ํ•ด๋‹น ์ •๊ทœ์‹์€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๊นŒ? ๋˜๋Š” ์–ด๋–ค ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๊นŒ? ์ •๊ทœ์‹ ๊ธฐ๋ฐ˜ ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ์„ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๊นŒ?

FWIW ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค ํƒœ๊ทธ ์ด๋ฆ„์„ ํ—ˆ์šฉํ•˜๊ณ  ์ž„์˜์˜ ํƒœ๊ทธ ์ด๋ฆ„์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

> require("hyperscript")("qjz").outerHTML
'<qjz></qjz>'

๋˜ํ•œ ํด๋ž˜์Šค ๋ฐ id ๊ฐ’์˜ ๋ฌด์ œํ•œ ํ˜ผํ•ฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

> require("hyperscript")("baz.foo#bar.qua").outerHTML
'<baz class="foo qua" id="bar"></baz>'

CSS ์„ ํƒ๊ธฐ์˜ ์œ ํšจ์„ฑ๋„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CSS ์„ ํƒ๊ธฐ ๋Š” ์ •๊ทœ์‹์œผ๋กœ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ์ •๊ทœ์‹์€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๊นŒ? ๋˜๋Š” ์–ด๋–ค ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๊นŒ? ์ •๊ทœ์‹ ๊ธฐ๋ฐ˜ ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ์„ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๊นŒ?

OP๋Š” ์•„๋‹ˆ์ง€๋งŒ HTMLDocument#createElement() ์˜ค๋ฒ„๋กœ๋“œ์™€ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ:

// ...
export declare function h(query: /^canvas([\.#]\w+)*$/): HTMLCanvasElement;
// ...
export declare function h(query: /^div([\.#]\w+)*$/): HTMLDivElement;
// ...

RE๊ฐ€ ๋ถˆ์™„์ „ํ•˜๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ทœ์น™์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๋งŽ์€ ์ปจํ…์ŠคํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” CSS ์„ ํƒ๊ธฐ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ณต์žกํ•œ ์„ ํƒ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ HTMLDocument.querySelector() ๊ฐ€ HTMLElement ๋ฅผ ํด๋ฐฑ์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ์™„๋ฒฝํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๊ณ  ์œ ์šฉํ•œ ๋น„ ์˜ค๋ฒ„๋กœ๋”ฉ ์˜ˆ์ œ๊ฐ€ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” TickerSymbol ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์ด ์žˆ๋Š” CCXT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ด ์ฃผ์„ ์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ •๊ทœ์‹ ํŒจํ„ด์„ ํ™•์ธํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋Š” ๋ณ„๋กœ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์ง€๋งŒ string ์˜ ํ•˜์œ„ ์œ ํ˜•์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ๋ฅผ ์›ํ•˜๋ฏ€๋กœ ๋” ์—„๊ฒฉํ•œ ํ• ๋‹น, ๋งค๊ฐœ๋ณ€์ˆ˜ ์œ ํ˜• ๊ฒ€์‚ฌ ๋“ฑ์„ ์–ป์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ๋•Œ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„์—๋Š” ๊ทธ๋ƒฅ ์ผ๋ฐ˜ ๋ฌธ์ž์—ด์ธ TickerSymbols, Currencies, Assets ๋“ฑ์„ ์ปดํŒŒ์ผ ํƒ€์ž„์— ์‰ฝ๊ฒŒ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@omidkrad ์ด๊ฒƒ์€ ์ •๊ทœ์‹ ๊ฒ€์ฆ ์œ ํ˜•์ด ์•„๋‹Œ ๋ช…๋ชฉ ์œ ํ˜• ์ด ํ•„์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค.

@m93a ์ œ ๊ฒฝ์šฐ์—๋Š” ๋ช…๋ชฉ ์œ ํ˜•์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ ๋™์ผํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ๊ฒฝ์šฐ ๋” ์—„๊ฒฉํ•œ ์œ ํ˜• ๊ฒ€์‚ฌ ๋ฐ ๋ฌธ์ž์—ด ์œ ํ˜• ์ž์ฒด ๋ฌธ์„œํ™”๋ฅผ ์œ„ํ•ด ์ •๊ทœ์‹ ๊ฒ€์ฆ ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CSS ์„ ํƒ๊ธฐ์˜ ์œ ํšจ์„ฑ๋„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CSS ์„ ํƒ๊ธฐ ๋Š” ์ •๊ทœ์‹์œผ๋กœ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ธ€์Ž„์š”, ์ •๊ทœ ํ‘œํ˜„์‹์ด ๊ทธ๊ฒƒ๋“ค์„ ํ•˜๋‚˜๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” CSS ์ •๊ทœ ํ‘œํ˜„์‹์„ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค..., ๊ทธ๋ ‡์ฃ ?

(์ดˆ์•ˆ) CSS ์œ ํ˜• ๊ฐ์ฒด ๋ชจ๋ธ

https://drafts.css-houdini.org/css-typed-om/

https://developers.google.com/web/updates/2018/03/cssom

๋ฌธ์ž์—ด ํ˜•์‹์˜ CSS ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ ค๋Š” ์š•๊ตฌ๋ฅผ ์ž ์žฌ์ ์œผ๋กœ ์™„ํ™”ํ•ฉ๋‹ˆ๋‹ค.

el.attributeStyleMap.set('padding', CSS.px(42));
const padding = el.attributeStyleMap.get('padding');
console.log(padding.value, padding.unit); // 42, 'px'

@RyanCavanaugh ํŠนํžˆ Mithril์˜ ๊ฒฝ์šฐ ํƒœ๊ทธ ์ด๋ฆ„์€ ^([^#\.\[\]]+) ์˜ ์บก์ฒ˜ ๊ทธ๋ฃน์„ ํ†ตํ•ด ์ถ”์ถœ๋˜์ง€๋งŒ(๊ธฐ๋ณธ๊ฐ’์€ "div" ), ์ผ์น˜ํ•˜๋Š” ^(${htmlTagNames.join("|")}) ์ด๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚ด ์ œ์•ˆ์„ ์‚ฌ์šฉ ํ•˜๋ฉด ์ด๊ฒƒ์ด ๋‚ด ๋ชฉ์ ์— ์ถฉ๋ถ„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

type SelectorAttrs = "" | `#${string}` | `.${string}`;

type GetTagName<T extends string> =
    T extends SelectorAttrs ? "div" :
    T extends `${keyof HTMLElementTagNameMap & (infer Tag)}${SelectorAttrs}` ? T :
    string;

์ด๋ฒคํŠธ ๋ฐ ์†์„ฑ์— ๊ด€ํ•ด์„œ๋Š” ์ผ๋‹จ ๋ถ€์ • ์œ ํ˜•์ด ๋„์ฐฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type EventsForElement<T extends Element> =
    T extends {addEventListener(name: infer N, ...args: any[]): any} ? N : never;

type MithrilEvent<E extends string> =
    (E extends EventsForElement<T> ? HTMLElementEventMap[E] : Event) &
    {redraw?: boolean};

type Attributes<T extends Element> =
    LifecycleAttrs<T> &
    {[K in `on${string}` & not LifecycleAttrs<T>](
        ev: K extends `on${infer E}` ? MithrilEvent<E> : never
    ): void | boolean} &
    {[K in keyof T & not `on${string}`]: T[K]} &
    {[K in string & not keyof T & not `on${string}`]: string};

BTW, ์ด ์›ํ™œํ•œ ํ†ตํ•ฉ๊ณผ ๋ณต์žก์„ฑ ํšŒํ”ผ๊ฐ€ ๋‚ด๊ฐ€ ์—ฌ์ „ํžˆ ๋ฆฌํ„ฐ๋Ÿด ์ •๊ทœ ํ‘œํ˜„์‹๋ณด๋‹ค ๋‚ด ์ œ์•ˆ์„ ์„ ํ˜ธํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.


ํ•˜์ง€๋งŒ ์ˆœ์ˆ˜ํ•œ ์ •๊ทœ ํ‘œํ˜„์‹ ์œ ํ˜•์œผ๋กœ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ง€์ ํ•˜๊ณ  ์‹ถ๋‹ค.

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

Bent ๋Š” ์˜ˆ์ƒ๋˜๋Š” ์‘๋‹ต ์œ ํ˜•์„ ์„ค๋ช…ํ•˜๋Š” ๋ฌธ์ž์—ด๋กœ ์ œ๊ณต๋œ ๊ฒƒ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.

bent('json')('https://google.com') // => Promise<JSON>
bent('buffer')('https://google.com') // => Promise<Buffer | ArrayBuffer>
bent('string')('https://google.com') // => Promise<String>

๋˜ํ•œ method ๋ฐ url๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ์ธ์ˆ˜๋ฅผ ๋ฌธ์ž์—ด๋กœ ํ—ˆ์šฉํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ์ธ์ˆ˜๋Š” ๋ชจ๋“  ์œ„์น˜์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  ๋ฐ˜ํ™˜ ์œ ํ˜•( 'json' | 'buffer' | 'string' )์„ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด ๊ณต์šฉ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๋Œ€์‹  ๋ฉ์ฒญํ•ด์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ๋‹ˆ์˜จ์˜ url ๋ฐ ๋ฉ”์„œ๋“œ ์œ ํ˜•๊ณผ ๊ฒฐํ•ฉํ•  ๋•Œ string ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์ฒซ ๋ฒˆ์งธ ํ˜ธ์ถœ์—์„œ ์ œ๊ณต๋œ ์œ ํ˜•์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ์ž๋™์œผ๋กœ ์œ ์ถ”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@Ovyerus ์ •๊ทœ์‹ ์œ ํ˜•์ด ์–ด๋–ป๊ฒŒ ๋„์›€์ด ๋ ๊นŒ์š”? ๋ฌด์—‡์„ ์“ธ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์‹ญ๋‹ˆ๊นŒ? ์˜ค๋ฒ„๋กœ๋“œ ๋˜๋Š” ์กฐ๊ฑด๋ถ€ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒคํŠธ์˜ ๋™์ž‘๊ณผ ์œ ์‚ฌํ•œ ๊ฒƒ์„ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type BentResponse<Encoding> = Promise<
    Encoding extends "json" ? MyJsonType :
    Encoding extends "buffer" ? Buffer | ArrayBuffer :
    Encoding extends "string" ? string :
    Response
>;

declare function bent<T extends string>(urlOrEncoding: T): (url: string) => BentResponse<T>;

http://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAQhB2wBKEDOYD29UQDwFF4BjDAEwEt4BzAPigF4oAFAJwwFtydcBYAKCiCohEhWpQIAD2AJSqKACIAVqiwKoAfigBZEAClV8ACrhoALn5DhxMpSoTps + QoBGAVwBmHiC3VaYnt4sUAA UACCLCwAhiABXj5QFgJCIrbiUjLwcoqowCx2flB5BeLJVijoWDj8NADc + - + PykEEQANtEs0B5uxMDkWFAuCMC4Rg5ZOSV2NAAUbiytAPIsaWJUZlBGAJQbcwsbU9RbDHRwiJWY2HhG9Y18lDIsHtFE0PFBUADeUAD67gksDbReAgKAAX34Dx8z1eOn0hhMkC vxUWCBIPBdxm0VQIGIUBmx3odE liErQgwCgkg2ugMWER0EY0QA7tFyFShogZspDAotjyABbAYBgVBmAD0Eqk0XYYApADoSOx + + + Q0 GCBVsgA

์•„, ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ๋ฌธ์ œ๋Š” ๊ธฐ๋ณธ URL์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ฌธ์ž์—ด ์‹œ์ž‘ ๋ถ€๋ถ„์— ์ผ์น˜ํ•˜๋Š” http(s): ๋ผ์ธ์— ๋” ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

Bent์˜ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

type HttpMethods = 'GET' | 'PATCH' | ...
type StatusCode = number;
type BaseUrl = string; // This is where I would ideally need to see if a string matches http(s):
type Headers = { [x: string]: any; };

type Options = HttpMethods | StatusCode | BaseUrl | Headers;

function bent(...args: Options[]): RequestFunction<RawResponse>
function bent(...args: (Options | 'json')[]): RequestFunction<JSON>
// and so on

๊ทธ๋Ÿฌ๋‚˜ BaseUrl ๋ฅผ ๋ฌธ์ž์—ด๋กœ ์‚ฌ์šฉํ•˜๋ฉด HttpMethods ๋ฐ ๋ฐ˜ํ™˜ ์œ ํ˜• ๊ณต์šฉ์ฒด๊ฐ€ ํก์ˆ˜๋˜์–ด string ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค. ๋ฌธ์ž์—ด๋กœ "๋ถ€์ ์ ˆํ•˜๊ฒŒ" ์‚ฌ์šฉํ•˜๋ฉด ๊ธฐ๋ณธ URL๋กœ ์‚ฌ์šฉํ•  ํ•ญ๋ชฉ์„ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด ^http: ๋˜๋Š” ^https: ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋ฏ€๋กœ ๊ตฌ๋ถ€๋Ÿฌ์ง„ ์ž‘๋™ ๋ฐฉ์‹๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

์ •๊ทœ์‹ ์œ ํ˜•์ด ์žˆ๋Š” ๊ฒฝ์šฐ BaseUrl์„ type BaseUrl = /^https?:/ ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด HTTP ๋ฉ”์„œ๋“œ ๋˜๋Š” ์‘๋‹ต ์ธ์ฝ”๋”ฉ์ด ์•„๋‹Œ ๋ฌธ์ž์—ด์„ ์ด์ƒ์ ์œผ๋กœ ํ™•์ธํ•˜๊ณ  string ๋กœ ํก์ˆ˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ ํ˜•.

์ •ํ™•ํžˆ๋Š” ์ €๋„ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค.

--
ํ”„๋กœ์ฝฅ ์‹œ๋ฉ•

2019๋…„ 10์›” 20์ผ 03:23:30, Michael Mitchell([email protected])
์ผ๋‹ค:

์˜ค, ๋‚˜๋Š” ๋‚ด ๋ฌธ์ œ๊ฐ€
์ผ์น˜ํ•˜๋Š” http(s): ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘ ๋ถ€๋ถ„์—์„œ ๊ธฐ๋ณธ URL์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Bent์˜ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์œ ํ˜• HttpMethods = '๊ฐ€์ ธ์˜ค๊ธฐ' | 'ํŒจ์น˜' | ...์œ ํ˜• StatusCode = ์ˆซ์ž;์œ ํ˜• BaseUrl = ๋ฌธ์ž์—ด; // ์—ฌ๊ธฐ์—์„œ ๋ฌธ์ž์—ด์ด http(s):type Headers = { [x: string]: any;์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ์ด์ƒ์ ์œผ๋กœ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. };
์œ ํ˜• ์˜ต์…˜ = HttpMethods | ์ƒํƒœ ์ฝ”๋“œ | ๊ธฐ๋ณธ URL | ํ—ค๋”;
ํ•จ์ˆ˜ ๋ฒคํŠธ(...args: ์˜ต์…˜[]): RequestFunctionํ•จ์ˆ˜ ๋ฒคํŠธ(...args: (์˜ต์…˜ | 'json')[]): RequestFunction// ๋“ฑ๋“ฑ

๊ทธ๋Ÿฌ๋‚˜ BaseUrl์„ ๋ฌธ์ž์—ด๋กœ ์‚ฌ์šฉํ•˜๋ฉด HttpMethods๋ฅผ ํก์ˆ˜ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
์œ ํ˜• ์œ ๋‹ˆ์˜จ์€ ๋ฌธ์ž์—ด๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์„ ๋ฌธ์ž์—ด๋กœ ๊ฐ–๋Š” ๊ฒƒ
๋˜ํ•œ "๋ถ€์ ์ ˆํ•˜๊ฒŒ" ๊ตฌ๋ถ€๋Ÿฌ์ง„ ์ž‘๋™ ๋ฐฉ์‹๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
^http: ๋˜๋Š” ^https: ์ค‘ ๋ฌด์—‡์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด
URL.

์ •๊ทœ์‹ ์œ ํ˜•์ด ์žˆ๋Š” ๊ฒฝ์šฐ BaseUrl์„ BaseUrl = /^https?:/ ์œ ํ˜•์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด์ƒ์ ์œผ๋กœ๋Š” HTTP ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹Œ ๋ฌธ์ž์—ด์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ™•์ธํ•˜๊ฑฐ๋‚˜
์‘๋‹ต ์ธ์ฝ”๋”ฉ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฌธ์ž์—ด ์œ ํ˜•์œผ๋กœ ํก์ˆ˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

โ€”
์ด ์Šค๋ ˆ๋“œ์— ๊ฐ€์ž…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ๋‹ต์žฅํ•˜๊ณ  GitHub์—์„œ ํ™•์ธ
https://github.com/microsoft/TypeScript/issues/6579?email_source=notifications&email_token=ABJ3U4JNK3V5MV4DJH73ZU3QPOXJFA5CNFSM4BZLAVSKYY1ABBYT0ZY01000003PNVWWK3TUL52HS4DFVREXG43VMVBW63
๋˜๋Š” ๊ตฌ๋… ์ทจ์†Œ
https://github.com/notifications/unsubscribe-auth/ABJ3U4PHBXO4766LK7P7UXDQPOXJFANCNFSM4BZLAVSA
.

์œ ์Šค ์ผ€์ด์Šค์— ๋Œ€ํ•œ ์ƒ๊ฐ์€ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜ ์œ ํ˜•์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ์‹๋ณ„์ž๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฌธ์ž์—ด์˜ ์ž˜ ์ •์˜๋œ ์ •๊ทœ์‹ ํ˜•์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํ™•์žฅ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ์‹๋ณ„์ž๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋ณตํ•ด์„œ ๋ง์”€๋“œ๋ฆฌ์ž๋ฉด, ์œ ํ˜•์ด ์ง€์ •๋œ ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋ ค๋Š” JavaScript ์ฝ”๋“œ์˜ ์˜ˆ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋ชจ๋ธ๋งํ•˜๋ ค๋Š” ๋Œ€์ƒ(๋ฐ ๋ชจ๋ธ๋ง ๋ฐฉ๋ฒ•์ด ์ด๋ฏธ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€)๋งŒ ์ถ”์ธกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@DanielRosenwasser ์•„๋ž˜๋Š” ํƒ€์ดํ•‘์„ ๊ฐ•์ œํ•˜๋ ค๋Š” ์ฝ”๋“œ์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค. http://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAqjCSARKBeKBnYAnAlgOwHMBYAKFIGMAbAQ3XVnQiygG9SoOoxcA3a4aJn45yULBGoATAPZ5KIKAFtq + GIywAuWAmRoA5NQCcEAAwBGQwCMArAFoAZtYBMAdlsAWcvYActw2ZM7JzNJF28TCCcANgtLPXZOAHpErl5 + QWBhUXEpWXklFTw1Ji04JFQoMycAZihkqAA5AHkAFSgAQQAZTqaAdQBRRASOeu4cPgEMTOARMQkZOQVlVXVSnQq9PGlgW2pbAFd9nEk9OpSAZQAJJphO5Ga2gCF + ju6 + ์™€์šฐ ++ BBL-oAlYZQciyTBYfbkYDSLAACkBnC4 + 0slFmxzWSAANHDOGBEcjRJYsNQ8JItKD8ARMSR4fCcUjZuocNRKFo8PtFJYmJTqdjcbNyDkBJJHiA0boGEwAHTLIrqACUrFICQAvqQVSQgA

@yannickglt RegExp ์œ ํ˜•์ด ์•„๋‹Œ ๋ช…๋ชฉ ์œ ํ˜•์„ ์›ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๊นŒ? ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž„์˜์˜ ์‚ฌ์ดํŠธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ˜ธ์ถœ์„ ํ˜ธ์ถœ์ž๊ฐ€ ํ‘œ์‹œํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

// OK
someFunc('a9e019b5-f527-4cf8-9105-21d780e2619b');
// Also OK, but probably really bad
someFunc('a9e019b5-f527-4cf8-9106-21d780e2619b');
// Error
someFunc('bfe91246-8371-b3fa-3m83-82032713adef');

์ •๊ทœ ํ‘œํ˜„์‹์œผ๋กœ UUID๋ฅผ ์„ค๋ช… ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์€ ๋ฌธ์ž์—ด ์ž์ฒด์˜ ํ˜•์‹์˜ ์ธ๊ณต๋ฌผ์ธ ๋ฐ˜๋ฉด, ํ‘œํ˜„ํ•˜๋ ค๋Š” ๊ฒƒ์€ UUID๊ฐ€ ์ง€์› ํ˜•์‹ ์ด ๋ฌธ์ž์—ด ์ธ ํŠน์ˆ˜ํ•œ ์ข…๋ฅ˜์˜ ์œ ํ˜•์ด๋ผ๋Š”

๋”ฐ๋ผ์„œ 3.7์˜ Assertion Functions ์™€ nominal ๊ธฐ๋Šฅ์˜ ์กฐํ•ฉ ์€ ์ด๊ฒƒ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(?)

nominal UUID = string

function someFunc(uuid: any): asserts uuid is UUID {
  if (!UUID_REGEX.test(uuid)) {
    throw new AssertionError("Not UUID!")
  }
}

class User {
  private static readonly mainUser: UUID = someFunc('a9e019b5-f527-4cf8-9105-21d780e2619b')
  // private static readonly mainUser: UUID = someFunc(123) // assertion fails
  // private static readonly mainUser: UUID = someFunc('not-a-uuid') // assertion fails
  constructor(
    public id: UUID,
    public brand: string,
    public serial: number,
    public createdBy: UUID = User.mainUser) {

  }
}

์ด๊ฒƒ๋„ ์‹คํŒจํ• ๊นŒ์š”?

new User('invalid-uuid', 'brand', 1) // should fail
new User('invalid-uuid' as UUID, 'brand', 1) // ๐Ÿค” 

์ž ์‹œ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€ ์ œ๊ฐ€ ์ œ์•ˆํ•œ ์†”๋ฃจ์…˜์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์–ด์š” ๐Ÿค”
asserts ๋Š” ๋Ÿฐํƒ€์ž„ ์—๋งŒ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค -> ๐Ÿ‘Ž
Regex-Validation ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„ ์˜ค๋ฅ˜๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -> ๐Ÿ‘
๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ด ์ œ์•ˆ์€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค

ํŽธ์ง‘ํ•˜๋‹ค:
๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ: someFunc(uuid: any): asserts uuid is UUID ๋Š” UUID๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  is UUID -> true ๋˜์ง€๊ฑฐ๋‚˜ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ UUID๋ฅผ mainUser ์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@RyanCavanaugh ์šฐ๋ฆฌ๋Š” Mithril์— ๋‹ค์Œ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž…๋ ฅํ•˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค.

// <div id="hello"></div>
m("div#hello", {
    oncreate(vnode) { const dom: HTMLDivElement = vnode.dom },
})

// <section class="container"></section>
m("section.container", {
    oncreate(vnode) { const dom: HTMLElement = vnode.dom },
})

// <input type="text" placeholder="Name">
m("input[type=text][placeholder=Name]", {
    oncreate(vnode) { const dom: HTMLInputElement = vnode.dom },
})

// <a id="exit" class="external" href="https://example.com">Leave</a>
m("a#exit.external[href='https://example.com']", {
    oncreate(vnode) { const dom: HTMLAnchorElement = vnode.dom },
}, "Leave")

// <div class="box box-bordered"></div>
m(".box.box-bordered", {
    oncreate(vnode) { const dom: HTMLDivElement = vnode.dom },
})

// <details></details> with `.open = true`
m("details[open]", {
    oncreate(vnode) { const dom: HTMLDetailsElement = vnode.dom },
})

// alias for `m.fragment(attrs, ...children)`
m("[", {
    oncreate(vnode) { const dom: HTMLElement | SVGElement = vnode.dom },
}, ...children)

์šฐ๋ฆฌ๋Š” ๋‹ค์Œ์„ ์ •์ ์œผ๋กœ ๊ฑฐ๋ถ€ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

// selector must be non-empty
m("")

// incomplete class
m("div.")

// incomplete ID
m("div#")

// incomplete attribute
m("div[attr=")

// not special and doesn't start /[a-z]/i
m("@foo")

์ด์ƒ์ ์œผ๋กœ๋Š” ์ •์ ์œผ๋กœ ๊ฑฐ๋ถ€ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์ง€ ์•Š์œผ๋ฉฐ ์—†์ด๋„ ์ƒ์กดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// event handers must be functions
m("div[onclick='return false']")

// `select.selectedIndex` is a number
m("select[selectedIndex='not a number']")

// `input.form` is read-only
m("input[type=text][form='anything']")

// `input.spellcheck` is a boolean, this evaluates to a string
// (This is a common mistake, actually.)
m("input[type=text][spellcheck=false]")

// invalid tag name for non-custom element
m("sv")

์ด๊ฒƒ์€ ํ›จ์”ฌ ๋” ๋ณต์žกํ•œ ์œ ํ˜• ์ •์˜๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ์œ ํ˜• ๊ฒ€์‚ฌ์— ์‹คํŒจํ•œ ์ด์œ ๋ฅผ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์œ ํ˜• ๊ฒ€์‚ฌ ์‹คํŒจ ๋ฉ”์‹œ์ง€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

react-hyperscript์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„์›Œํฌ๋„ ๋น„์Šทํ•œ ๋ฌธ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

@isiahmeadows ๋Š” ์˜ฌ๋ฐ”๋ฅธ ์ž…๋ ฅ๊ณผ ํ•จ๊ป˜ ๋ธŒ๋žœ๋“œ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ผ์ข…์˜ ์„ ํƒ๊ธฐ ๋ฌธ์ž์—ด ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ข‹๋‹ค:

m(mt.div({ attr1: 'val1' }))

@anion155 ๊ฑฐ๊ธฐ์— m("div", {...attrs}, ...children) ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ดํผ์Šคํฌ๋ฆฝํŠธ ์„คํƒ•์€ ์—†์ง€๋งŒ(์ž…๋ ฅํ•˜๊ธฐ ์‰ฝ๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ๊ฐ€ ํ›จ์”ฌ ๋” ๊ฐ„๋‹จํ•จ) ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋งŽ์€ ๊ฒƒ์„ ํ•˜๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋Šฆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•  ๋ง์ด ๋งŽ์Šต๋‹ˆ๋‹ค . ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ฐธ์„์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ œ ์ƒ๊ฐ์„ ์กฐ๊ธˆ์”ฉ ํ’€์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค.

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -542405537

"์ •๋ฐ€์—ผ"(๋‚จ์ž, ๋‚˜๋Š” ๊ทธ ๋‹จ์–ด๋ฅผ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค)์— ๋Œ€ํ•ด,
๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋„ˆ๋ฌด ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์œ ํ˜• ์‹œ์Šคํ…œ์€ ์ด๋ฏธ ํŠœ๋ง์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ๋งŽ์€ ๊ฒƒ์— ๋Œ€ํ•ด ๋งค์šฐ ์ •ํ™•ํ•  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
(์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“  SQL์„ ๋ชจ๋ธ๋ง? ๋ป”๋ป”ํ•œ ํ”Œ๋Ÿฌ๊ทธ = P)

๊ทธ๋Ÿฌ๋‚˜ (๋„ˆ๋ฌด ๋งŽ์€) ์‚ฌ๋žŒ๋“ค์ด ์ด๋ ฅ์„ ๊ธฐ์šธ์ด๊ณ  ๋ชจ๋“  ์œ ํ˜• ์—ฐ์‚ฐ์ž๋ฅผ ๋ฏธ์นœ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์„œ๋กœ ํ˜ธํ™˜๋˜๋Š” ๊ฒƒ์„ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์„ ๋ณด์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋„์„œ๊ด€ ์ž‘๊ฐ€๋“ค์ด ์ถฉ๋ถ„ํžˆ ๋ƒ‰์ •ํ•œ ๊ฒฝํ–ฅ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค... ๋งž์ฃ ?

๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•/์ •๊ทœ์‹ ์œ ํšจ์„ฑ์ด ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•์„ ์›ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ํ”ํ•˜์ง€ ์•Š์ง€๋งŒ ํ™•์‹คํžˆ ๋‚ด ์ฝ”๋“œ ๊ธฐ๋ฐ˜์˜ ์œ ํ˜• ์•ˆ์ „์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.


์‚ฌ์šฉ ์‚ฌ๋ก€

๋จธ๋ฆฌ ๊ผญ๋Œ€๊ธฐ์—์„œ ์ตœ๊ทผ ํ•œ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋” ๋งŽ๊ฒ ์ง€๋งŒ ๋‚˜๋Š” ๊ฑด๋ง์ฆ์ธ)

์ŠคํŠธ๋ผ์ดํ”„์˜ API (์ง€๋ถˆ ์ฒ˜๋ฆฌ ํ”Œ๋žซํผ)๊ณผ ํ†ตํ•ฉ ํ•  ๋•Œ, ์‚ฌ์šฉ ch_ ์— ๋Œ€ํ•ด charge , - ๊ด€๋ จ ์‹๋ณ„์ž re_ ์— ๋Œ€ํ•ด refund - ๊ด€๋ จ ์‹๋ณ„์ž ๋“ฑ

PatternOf</^ch_.+/> ๋ฐ PatternOf</^re_.+/> ๋กœ ์ธ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์‹์œผ๋กœ ์˜คํƒ€๋ฅผ ๋งŒ๋“ค ๋•Œ,

charge.insertOne({ stripeChargeId : someObj.refundId });

๋‚˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์–ป์„ ๊ฒƒ์ด๋‹ค,

Cannot assign `PatternOf</^re_.+/>` to `PatternOf</^ch_.+/>`

๋‚ด๊ฐ€ ๋ช…๋ชฉํ˜•/ํƒœ๊ทธํ˜• ์œ ํ˜•์„ ์ข‹์•„ํ•˜๋Š” ๋งŒํผ ์ธ์ฒด๊ณตํ•™์ ์ด์ง€ ์•Š๊ณ  ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” ํ•ญ์ƒ ๋ช…๋ชฉํ˜•/ํƒœ๊ทธํ˜• ์œ ํ˜•์„ ์ตœํ›„์˜ ์ˆ˜๋‹จ ์œผ๋กœ ๋ด…๋‹ˆ๋‹ค. TS ์œ ํ˜• ์‹œ์Šคํ…œ์ด ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์—†๋Š” ๋ฌด์–ธ๊ฐ€๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” ์œ ํ˜•์€ ํŒฌํ…€ ์œ ํ˜•์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
๋ช…๋ชฉํ˜•์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฒฐ์ฝ” ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
(์ข‹์•„์š”, ์ œ๊ฐ€ ํŽธํ–ฅ๋œ ๊ฒƒ์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ unique symbol ๋•Œ๋ฌธ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ €๋Š” ์ œ๊ฐ€ ์™„์ „ํžˆ ํ‹€๋ฆฌ์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.)

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ "ValueObject" ํŒจํ„ด์€ ํ›จ์”ฌ ๋” ๋‚˜์ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.


๋น„๊ต

์•„๋ž˜์—์„œ ๋‹ค์Œ์„ ๋น„๊ตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•/์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ฌธ์ž์—ด ์œ ํ˜•
  • ํ˜ธ์นญ ์œ ํ˜•
  • ๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•

"ValueObject" ํŒจํ„ด์ด ์ตœ์•…์˜ ์†”๋ฃจ์…˜์ด๋ผ๋Š” ๋ฐ ๋ชจ๋‘ ๋™์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋น„๊ต ์‹œ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ฃ ?


๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;
type StripeRefundId = PatternOf<typeof stripeRefundIdRegex>;

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (stripeChargeIdRegex.test(str)) {
  takesStripeChargeId(str); //OK
}
if (stripeRefundIdRegex.test(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //OK
takesStripeChargeId("re_hello"); //Error

์ €๊ฒƒ ๋ด.

  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.
  • string ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋„ ๋‚˜์˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ช…๋ชฉํ˜•...

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = make_unique_type string;
type StripeRefundId = make_unique_type string;

function isStripeChargeId (str : string) : str is StripeChargeId {
  return stripeChargeIdRegex.test(str);
}
function isStripeRefundId (str : string) : str is StripeRefundId {
  return stripeRefundIdRegex.test(str);
}

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (isStripeChargeId(str)) {
  takesStripeChargeId(str); //OK
}
if (isStripeRefundId(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //Error? Ughhhh
takesStripeChargeId("re_hello"); //Error

takesStripeChargeId("ch_hello" as StripeChargeId); //OK, BUT UNSAFE
takesStripeChargeId("re_hello" as StripeChargeId); //OK, BUT WAIT! I MESSED UP

const iKnowThisIsValid = "ch_hello";
if (isStripeChargeId(iKnowThisIsValid)) {
  takesStripeChargeId(iKnowThisIsValid); //OK
} else {
  throw new Error(`Wat? This should be valid`);
}

function assertsStripeChargeId (str : string) : asserts str is StripeChargeId {
  if (!isStripeChargeId(str)) {
    throw new Error(`Expected StripeChargeId`);
  }
}
assertsStripeChargeId(iKnowThisIsValid);
takesStripeChargeId(iKnowThisIsValid); //OK

function makeStripeChargeIdOrError (str : string) : StripeChargeId {
  assertsStripeChargeId(str);
  return str;
}
takesStripeChargeId(makeStripeChargeIdOrError("ch_hello")); //OK
takesStripeChargeId(makeStripeChargeIdOrError("re_hello")); //OK, compiles, throws during run-time... Not good

์ €๊ฒƒ ๋ด.

  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ๊ฒฝ์šฐ TERRIBLE .
  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์žฅ์• ๋ฅผ ๊ทน๋ณตํ•˜๊ณ  ๋‚˜๋ฉด ๋‚˜์˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ... ๋งž์Šต๋‹ˆ๊นŒ?

๊ทธ๋Ÿฌ๋‚˜ ์ด ์ œ์•ˆ์˜ ์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์ž…๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ด๊ฒƒ์€ ๋”์ฐํ•œ ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค.


๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•...

๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์€ ๋ช…๋ชฉ ์œ ํ˜•๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค...

const stripeChargeIdRegex = /^ch_.+/;
const stripeRefundIdRegex = /^re_.+/;

type StripeChargeId = string & tag { stripeChargeId : void };
type StripeRefundId = string & tag { stripeRefundId : void };

function isStripeChargeId (str : string) : str is StripeChargeId {
  return stripeChargeIdRegex.test(str);
}
function isStripeRefundId (str : string) : str is StripeRefundId {
  return stripeRefundIdRegex.test(str);
}

declare function takesStripeChargeId (stripeChargeId : StripeChargeId) : void;

declare const str : string;
takesStripeChargeId(str); //Error
if (isStripeChargeId(str)) {
  takesStripeChargeId(str); //OK
}
if (isStripeRefundId(str)) {
  takesStripeChargeId(str); //Error
}

declare const stripeChargeId : StripeChargeId;
declare const stripeRefundId : StripeRefundId;
takesStripeChargeId(stripeChargeId); //OK
takesStripeChargeId(stripeRefundId); //Error

takesStripeChargeId("ch_hello"); //Error? Ughhhh
takesStripeChargeId("re_hello"); //Error

takesStripeChargeId("ch_hello" as StripeChargeId); //OK, BUT UNSAFE
takesStripeChargeId("re_hello" as StripeChargeId); //OK, BUT WAIT! I MESSED UP

const iKnowThisIsValid = "ch_hello";
if (isStripeChargeId(iKnowThisIsValid)) {
  takesStripeChargeId(iKnowThisIsValid); //OK
} else {
  throw new Error(`Wat? This should be valid`);
}

function assertsStripeChargeId (str : string) : asserts str is StripeChargeId {
  if (!isStripeChargeId(str)) {
    throw new Error(`Expected StripeChargeId`);
  }
}
assertsStripeChargeId(iKnowThisIsValid);
takesStripeChargeId(iKnowThisIsValid); //OK

function makeStripeChargeIdOrError (str : string) : StripeChargeId {
  assertsStripeChargeId(str);
  return str;
}
takesStripeChargeId(makeStripeChargeIdOrError("ch_hello")); //OK
takesStripeChargeId(makeStripeChargeIdOrError("re_hello")); //OK, compiles, throws during run-time... Not good

์ €๊ฒƒ ๋ด.

  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ๊ฒฝ์šฐ TERRIBLE .
  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์žฅ์• ๋ฅผ ๊ทน๋ณตํ•˜๊ณ  ๋‚˜๋ฉด ๋‚˜์˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ... ๋งž์Šต๋‹ˆ๊นŒ?

๊ทธ๋Ÿฌ๋‚˜ ์ด ์ œ์•ˆ์˜ ์ฃผ์š” ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์ž…๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ด๊ฒƒ์€ ๋”์ฐํ•œ ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ ์ด ๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜• ์˜ˆ์ œ๋Š” ๋ช…๋ชฉ ์œ ํ˜• ์˜ˆ์ œ์˜ ๋ฆฌํ„ฐ๋Ÿด(ha, pun) ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ ์ž…๋‹ˆ๋‹ค.

์œ ์ผํ•œ ์ฐจ์ด์ ์€ StripeChargeId ๋ฐ StripeRefundId ์ด ์„ ์–ธ๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ ๊ตฌ์กฐ์  ์œ ํ˜•์ด ๋ช…๋ชฉ ์œ ํ˜•๋ณด๋‹ค ๋‚ซ์Šต๋‹ˆ๋‹ค. (๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ ์ด ์ ์„ ๋ช…ํ™•ํžˆ ํ•˜๊ฒ ๋‹ค, ๋งน์„ธํ•œ๋‹ค).


๊ฒฐ๋ก 

์ด ๋Œ“๊ธ€์˜ ๊ฒฐ๋ก ์ž…๋‹ˆ๋‹ค! ๋‚ด ์ „๋ฐ˜์ ์ธ ์ƒ๊ฐ์— ๋Œ€ํ•œ ๊ฒฐ๋ก ์ด ์•„๋‹™๋‹ˆ๋‹ค!

๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•/์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ฌธ์ž์—ด ์œ ํ˜• ์€ ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜• ๋„ˆ๋ฌด ์ธ์œ„์  ์ด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์ถฉ๋ถ„ํžˆ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.


๊ฒฐ๋ก (์ถ”๊ฐ€)

๊ฐ€๋Šฅํ•œ ํ•œ ๊ธฐ๋ณธ ์œ ํ˜•์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์ด ํ•ญ์ƒ ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ/๊ฐ’-๊ฐ์ฒด ์œ ํ˜•๋ณด๋‹ค ์„ ํ˜ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์œ ํ˜•์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ทจํ•˜๋Š” ์˜ˆ,

  • string ๋ฆฌํ„ฐ๋Ÿด
  • number ๋ฆฌํ„ฐ๋Ÿด( NaN, Infinity, -Infinity ์ œ์™ธ)
  • boolean ๋ฆฌํ„ฐ๋Ÿด
  • bigint ๋ฆฌํ„ฐ๋Ÿด
  • unique symbol ๋„ symbol ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ์˜ˆ ์ค‘ boolean ๋งŒ "์œ ํ•œ"ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€ ๊ฐ’๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐœ๋ฐœ์ž๋Š” true ๋ฐ false ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋งŒ์กฑํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋” ์ด์ƒ ์š”๊ตฌํ•  ๊ฒƒ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


number ์œ ํ˜•์€ ์œ ํ•œํ•˜์ง€๋งŒ ๊ฐ’์ด ๋„ˆ๋ฌด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๋ฌดํ•œํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๊ฐ€ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌํ„ฐ๋Ÿด์—๋„ ๊ตฌ๋ฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ฒ”์œ„ ๋ฒˆํ˜ธ ์œ ํ˜• ๋ฐ NaN, Infinity, -Infinity ๋ฌธ์ œ๊ฐ€ ์ธ๊ธฐ๊ฐ€ ์žˆ๊ณ  ๊ณ„์† ๋‚˜ํƒ€๋‚˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๋ฌดํ•œ ์ง‘ํ•ฉ์—์„œ ์ž‘์€ ์œ ํ•œ ๊ฐ’ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ๋ฌดํ•œ ์ง‘ํ•ฉ์˜ ํฐ ์œ ํ•œ/๋ฌดํ•œ ๋ถ€๋ถ„ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•ด์•ผ ํ•  ๋•Œ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์˜ค๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ์•„์ด๋””์–ด ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.


bigint ์œ ํ˜•์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌดํ•œํ•˜๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ํ•ด์„œ๋งŒ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ฒ”์œ„ ๋ฒˆํ˜ธ ์œ ํ˜• ๋ฌธ์ œ์˜ ์ธ๊ธฐ์— ๊ธฐ์—ฌํ•ฉ๋‹ˆ๋‹ค.


string ์œ ํ˜•์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฌดํ•œํ•˜๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ํ•ด์„œ๋งŒ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ฐ”๋กœ ์ด ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•/์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ฌธ์ž์—ด ์œ ํ˜• ๋ฌธ์ œ๊ฐ€ ์ธ๊ธฐ ์žˆ๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

์ •๊ทœ์‹์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ๋ฌดํ•œ ์ง‘ํ•ฉ์˜ ํฐ ์œ ํ•œ/๋ฌดํ•œ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•ด์•ผ ํ•  ๋•Œ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์˜ค๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ์•„์ด๋””์–ด ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.


symbol ํƒ€์ž…... ์—ญ์‹œ ๋ฌดํ•œ๋Œ€์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฑฐ์˜ ๋ฌด์ œํ•œ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ symbol ์œ ํ˜•์˜ ์š”์†Œ๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ๋ฉด์—์„œ ์„œ๋กœ ๊ฑฐ์˜ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ฌด๋„ " symbol ์˜ ํฐ ์œ ํ•œ/๋ฌดํ•œ ๋ถ€๋ถ„ ์ง‘ํ•ฉ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?"๋ผ๊ณ  ๋ฌป๋Š” ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๊ทธ ์งˆ๋ฌธ์€ ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜๋ฏธ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—†์Šต๋‹ˆ๋‹ค(๋งž๋‚˜์š”?)


๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ๋ณธ ์š”์†Œ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๊ทธ๋‹ค์ง€ ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค,

  • ์ถ”๊ฐ€ ์ž‘์—… ์—†์ด ์˜ฌ๋ฐ”๋ฅธ ์œ ํ˜•์˜ ๋ฆฌํ„ฐ๋Ÿด์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๋ง™๊ฒŒ๋„ TS๋Š” ์ด๋ฅผ ํ—ˆ์šฉํ•  ๋งŒํผ ์ œ์ •์‹ ์ด ์•„๋‹™๋‹ˆ๋‹ค.

false ๋ฅผ (arg : false) => void ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์ƒ์ƒํ•ด๋ณด์„ธ์š”!

  • ์ขํžˆ๋Š” ๋ฐฉ๋ฒ• ๋‚ด์žฅ

    ํ˜„์žฌ ์ด๋Ÿฌํ•œ ๋ฆฌํ„ฐ๋Ÿด์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ ์ œ๊ณต ์ถ•์†Œ ๋ฐฉ์‹์œผ๋กœ == & === ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๊ฐ ๋ฆฌํ„ฐ๋Ÿด์— ๋Œ€ํ•ด ์ƒˆ๋กœ์šด ์œ ํ˜• ๊ฐ€๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ์ƒํ•ด๋ณด์„ธ์š”!

๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ/๊ฐ’-๊ฐ์ฒด ์œ ํ˜•์˜ ๋ฌธ์ œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์œ„์˜ ๋‘ ๊ฐ€์ง€ ๊ธฐ์ค€์„ ์ถฉ์กฑํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ์›์‹œ ์œ ํ˜•์„ ๊ฐ์ฒด ์œ ํ˜•์ด ์•„๋‹ˆ์ง€๋งŒ ์–ด์จŒ๋“  ๊ฐ์ฒด ์œ ํ˜•์ฒ˜๋Ÿผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ํˆฌ๋ฐ•ํ•œ ์œ ํ˜•์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

์ธ๊ฐ„ ๊ณตํ•™

์ž, ์—ฌ๊ธฐ์— ๋ฌธ์ž์—ด ํŒจํ„ด ๋Œ€ ๋ช…๋ชฉ ํƒœ๊ทธ ์œ ํ˜• ๋ฐ ๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์„ค๋ช…์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ธ์ˆ˜๋Š” https://github.com/microsoft/TypeScript/issues/15480 ์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.


๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ

๋ช…๋ชฉํ˜•์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ ์ด ์ตœ์•… ์ž…๋‹ˆ๋‹ค.
๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ unique symbol ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ƒํ˜ธ ์šด์šฉํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๊ทธ๊ฒƒ์€ ๋‹จ์ˆœํžˆ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
์ƒ์šฉ๊ตฌ ์œ ํ˜• ๊ฐ€๋“œ ๋˜๋Š” trust-me-operator( as )๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์–ด์„ค์…˜ ๊ฐ€๋“œ์—๋„ ๋” ๋งŽ์€ ์ƒ์šฉ๊ตฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์œ ํ˜•์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ช…๋ชฉ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค...
๋งค์šฐ ์ธ์ฒด๊ณตํ•™์ ์ด์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋„(์œ„์˜ ์˜ˆ ์ฐธ์กฐ).


๊ตฌ์กฐ ์œ ํ˜•์˜ ๊ฒฝ์šฐ A ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์žˆ๋Š” ๊ฒฝ์šฐ

//Lowercase 'S'
type StripeChargeId = string & tag { stripeChargeId : void };

B ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š”

//Uppercase 'S'
type StripeChargeId = string & tag { StripeChargeId : void };

//Or
type StripeChargeId = string & tag { isStripeChargeId : true };

//Or
type StripeChargeId = string & tag { stripe_charge_id : void };

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒ์šฉ๊ตฌ ์œ ํ˜• ๊ฐ€๋“œ ๋˜๋Š” trust-me-operator( as )๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์–ด์„ค์…˜ ๊ฐ€๋“œ์—๋„ ๋” ๋งŽ์€ ์ƒ์šฉ๊ตฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์œ ํ˜•์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ตฌ์กฐ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค...
๋งค์šฐ ์ธ์ฒด๊ณตํ•™์ ์ด์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋„(์œ„์˜ ์˜ˆ ์ฐธ์กฐ).


๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์˜ ๊ฒฝ์šฐ A ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์žˆ๋Š” ๊ฒฝ์šฐ

type stripeChargeIdRegex = /^ch_.+/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

B ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š”

//Extra dollar sign at the end
type stripeChargeIdRegex = /^ch_.+$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

//Or,
type stripeChargeIdRegex =/^ch_[a-zA-Z0-9]$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

//Or,
type stripeChargeIdRegex =/^ch_[A-Za-z0-9]$/;
type StripeChargeId = PatternOf<typeof stripeChargeIdRegex>;

๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชจ๋‘ ํ•ญ์ƒ ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๋Š” StripeChargeId ๋Œ€ํ•œ ๋ฌธ์ž์—ด์„ ์ƒ์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. A ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ํ•จ๊ป˜ "๊ฒŒ์œผ๋ฅธ" ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  B ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ "๋” ์—„๊ฒฉ"ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์ข€ ์งœ์ฆ๋‚ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚˜์˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
libraryB.stripeChargeIdRegex.test(libraryA_stripeChargeId) ๋ฅผ typeguard๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. trust-me-operator( as )๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ฃผ์žฅ ๋ณดํ˜ธ๋ฅผ ์œ„ํ•œ ์ƒ์šฉ๊ตฌ๋Š” ์—ฌ์ „ํžˆ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์œ ํ˜•์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์™„๋ฒฝํ•˜๊ณ  ์ธ์ฒด๊ณตํ•™์ ์ž…๋‹ˆ๋‹ค.


๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„ ํ˜ธํ™˜์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์ด ๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜•๋ณด๋‹ค ์—ฌ์ „ํžˆ ์ข‹์Šต๋‹ˆ๋‹ค! ๋‚ด ๋ง์„ ๋“ค์–ด.

๋ชจ๋ธ๋ง๋˜๋Š” ๋„๋ฉ”์ธ์ด ์ž˜ ์ดํ•ด๋˜๋ฉด ์—ฌ๋Ÿฌ ๋ช…์˜ ๊ฒฉ๋ฆฌ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž๊ฐ€ ๋™์ผํ•œ ์ •๊ทœ์‹์„ ์ž‘์„ฑํ•˜๊ฒŒ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋งค์šฐ ๋†’์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ํƒœ๊ทธ์— ์›ํ•˜๋Š” ์†์„ฑ๊ณผ ์œ ํ˜•์„ ๋ชจ๋‘ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋ธ๋ง๋˜๋Š” ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•œ ๋ฌธ์ž์—ด ํ˜•์‹์„ ์ง€์ •ํ•˜๋Š” ํ‘œ์ค€ ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž๊ฐ€ ๋™์ผํ•œ ์ •๊ทœ์‹์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค! ๋‹ค๋ฅธ ์ •๊ทœ์‹์„ ์ž‘์„ฑํ•˜๋ฉด ์‹ค์ œ๋กœ ํ‘œ์ค€์„ ๋”ฐ๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ์ „ํžˆ ๋ฌด์—‡์ด๋“  ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ด€์‹ฌ์„ ๊ฐ€์งˆ ๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜• ํ‘œ์ค€์„ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š” ํ•œ? ใ…‹ใ…‹)


๋ฒ„์ „ ๊ฐ„ ํ˜ธํ™˜์„ฑ

ํ‰์†Œ์™€ ๊ฐ™์ด ๋ช…๋ชฉ ์œ ํ˜•์€ ๋ฒ„์ „ ๊ฐ„ ํ˜ธํ™˜์„ฑ ์ด ๊ฐ€์žฅ ๋‚ฎ ์Šต๋‹ˆ๋‹ค.
์˜ค, ๋‹น์‹ ์€ ๋‹น์‹ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํŒจ์น˜ ๋˜๋Š” ๋งˆ์ด๋„ˆ ๋ฒ„์ „์— ๋ถ€๋”ช์ณค์Šต๋‹ˆ๊นŒ?
ํ˜•์‹ ์„ ์–ธ์ด ์—ฌ์ „ํžˆ ๋™์ผํ•ฉ๋‹ˆ๊นŒ?
์ฝ”๋“œ๋Š” ์—ฌ์ „ํžˆ ๋™์ผํ•ฉ๋‹ˆ๊นŒ?
์•„๋‹ˆ์š”. ๊ทธ๋“ค์€ ๋‹ค๋ฅธ ์œ ํ˜•์ž…๋‹ˆ๋‹ค.

image


๊ตฌ์กฐ์  ํƒœ๊ทธ ์œ ํ˜•์€ ํƒœ๊ทธ ์œ ํ˜•์ด ๊ตฌ์กฐ์ ์œผ๋กœ ๋™์ผํ•œ ํ•œ ์—ฌ๋Ÿฌ ๋ฒ„์ „(์ฃผ์š” ๋ฒ„์ „ ํฌํ•จ)์—์„œ ์—ฌ์ „ํžˆ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์€ regex๊ฐ€ ๋™์ผํ•œ ํ•œ ๋ฒ„์ „(์ฃผ์š” ๋ฒ„์ „ ํฌํ•จ) ๊ฐ„์— ์—ฌ์ „ํžˆ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋‹ˆ๋ฉด ์ •๊ทœ์‹์ด ๋™์ผํ•œ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด PSPACE-์™„์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ์ •๊ทœ์‹์˜ ์–ด๋–ค ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด์— ๋Œ€ํ•ด ์ตœ์ ํ™”๋œ ๋“ฑ๊ฐ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค.

Regex ํ•˜์œ„ ์œ ํ˜• ๊ฒ€์‚ฌ๋Š” ์žˆ์œผ๋ฉด ๋ฉ‹์ง€๊ณ  ํ™•์‹คํžˆ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์„ ๋” ์ธ์ฒด ๊ณตํ•™์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฒ”์œ„ ํ•˜์œ„ ์œ ํ˜• ๊ฒ€์‚ฌ๊ฐ€ ์ˆซ์ž ๋ฒ”์œ„ ์œ ํ˜• ์ œ์•ˆ์— ๋„์›€์ด ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ.

[ํŽธ์ง‘ํ•˜๋‹ค]
์ด ๋Œ“๊ธ€์—์„œ,
https://github.com/microsoft/TypeScript/issues/6579#issuecomment -243338433

์—ฐ๊ฒฐ๋œ ๋ˆ„๊ตฐ๊ฐ€,
https://bora.uib.no/handle/1956/3956

์ œ๋ชฉ "์ •๊ทœ ํ‘œํ˜„์‹์˜ ํฌํ•จ ๋ฌธ์ œ"
[/ํŽธ์ง‘ํ•˜๋‹ค]


์ƒ์šฉ๊ตฌ

TODO (ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์ด ๊ฐ€์žฅ ์ ์€ ์–‘์˜ ์ƒ์šฉ๊ตฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

๋ฆฌํ„ฐ๋Ÿด ํ˜ธ์ถœ

TODO (ํ•˜์ง€๋งŒ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์ด ๋ฆฌํ„ฐ๋Ÿด ํ˜ธ์ถœ์„ ๊ฐ€์žฅ ์ž˜ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ํ˜ธ์ถœ

TODO (ํ•˜์ง€๋งŒ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์ด ๋น„๋ฌธ์ž ํ˜ธ์ถœ์„ ๊ฐ€์žฅ ์ž˜ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)

https://github.com/microsoft/TypeScript/issues/6579#issuecomment -542405537์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด

TypeScript๋Š” ๊ต์ฐจ์˜ ์ธ์Šคํ„ด์Šคํ™”์—์„œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ด๊ฒƒ์€ ์ตœ์ข… ๋””์ž์ธ์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

์‚ฌ๋žŒ๋“ค์ด ๊ต์ฐจ๋กœ๋ฅผ ๊ธˆ์ง€ํ•˜๋ ค๋Š” ์ด์œ ๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๊ธˆ์ง€ํ•˜๋Š” ๊ฒƒ์ด ๋ง์ด ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์€ ์ „์ ์œผ๋กœ ์˜ณ์Šต๋‹ˆ๋‹ค.


๋”ฐ๋ผ์„œ ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ๋ชจ๋“  ํ˜ธ์ถœ์„ ๊นจ๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

๊ธ€์Ž„, ๋ชจ๋“  ๋น„ ๋ฆฌํ„ฐ๋Ÿด ํ˜ธ์ถœ์ด ์•„๋‹™๋‹ˆ๋‹ค.

declare function foo (arg : PatternOf</a+/>) : void;
function bar (arg : PatternOf</a+/>) : void {
  //non-literal and does not break.
  foo(arg);
}
bar("aa"); //OK
bar("bb"); //Error
bar("" as string); //Error, I know this is what you meant by non-literal invocation

function baz (arg : "car"|"bar"|"tar") : void {
  bar(arg); //OK
}

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

์ด๋Š” ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ํ˜ธ์ถœ์ด ์‹คํŒจํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์ด ๋‚˜์˜๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•/์ •๊ทœ์‹ ๊ฒ€์ฆ ๋ฌธ์ž์—ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌดํ•œํ•œ ์ˆ˜์˜ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์˜ ํ•ฉ์ง‘ํ•ฉ์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ์‚ฌ์šฉ์€ ์žฌํ…Œ์ŠคํŠธ ๋˜๋Š” ์ฃผ์žฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ „ํ˜€ ๋ฌธ์ œ๋กœ ๋ณด์ง€ ์•Š๋Š”๋‹ค.
์ง€๊ธˆ์€ ๋ช…๋ชฉ/ํƒœ๊ทธ ์œ ํ˜•๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
๋˜๋Š” ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์˜ˆ์ƒํ•˜๋Š” ํ•จ์ˆ˜์— string ๋ฅผ ์ „๋‹ฌํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
๋˜๋Š” ๋” ๋„“์€ ์œ ํ˜•์„ ๋” ์ข์€ ์œ ํ˜•์œผ๋กœ ์ „๋‹ฌํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ์— const ZipCode = /^\d\d\d\d\d$/; ๋ฐ ZipCode.test(s) ๊ฐ€ ์œ ํ˜• ๊ฐ€๋“œ ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ™•์‹คํžˆ ์ธ์ฒด ๊ณตํ•™์— ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.


  • ํ•ด๊ฒฐ๋˜๊ณ  ์žˆ๋Š” ๋ฌธ์ œ์—๋Š” ๋” ๋‚˜์€ ๋Œ€์•ˆ์ด ์—†์Šต๋‹ˆ๋‹ค(์•„์ง ์–ธ์–ด์— ์—†๋Š” ๊ทธ๋Ÿด๋“ฏํ•œ ๋Œ€์•ˆ ํฌํ•จ).

๊ธ€์Ž„, ๋‚˜๋Š” ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์ด ๋” ๋‚˜์€ ๋Œ€์•ˆ์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ์‹ค์ œ๋กœ ๊ฝค ๋‚˜์ฉ๋‹ˆ๋‹ค.

  • ์‹ค์ œ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์˜๋ฏธ ์žˆ๋Š” ๋นˆ๋„๋กœ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์–ด... ๊ทธ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋‹ค์‹œ ๋ง์”€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค...

  • ์ œ์•ˆ๋œ ์†”๋ฃจ์…˜์€ ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ์ž˜ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

์ œ์•ˆ๋œ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์€ ๊ฝค ๊ดœ์ฐฎ์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

๋‹น์‹ ์˜ ๊ฒฌํ•ด๋Š” ๋ช…๋ชฉ/ํƒœ๊ทธ๊ฐ€ ๋ถ™์€ ์œ ํ˜•์ด ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹Œ ์‚ฌ์šฉ์— ์ถฉ๋ถ„ํ•˜๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹Œ ์‚ฌ์šฉ์„ ๋ณด์—ฌ ์ฃผ๋Š” ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋ช…๋ชฉ/ํƒœ๊ทธ ์œ ํ˜•์ด ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„

  • ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/๋ฒ„์ „ ํ˜ธํ™˜์„ฑ ๋ฌธ์ œ๋กœ ์–ด๋ ค์›€์„ ๊ฒช์Šต๋‹ˆ๋‹ค.
  • ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์— ๋Œ€ํ•œ ์ƒ์šฉ๊ตฌ์˜ ์–‘์ด ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์— ๋Œ€ํ•œ ์ƒ์šฉ๊ตฌ๋ณด๋‹ค ํ›จ์”ฌ ๋งŽ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ์ œ๊ธฐ๋œ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ์ด๋ฉ”์ผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ๊ฐ™์€ ์–ด๋ฆฌ์„์€ ์ผ์„ ์‹œ๋„ํ•˜๊ฑฐ๋‚˜ ์ถฉ๋ถ„ํžˆ ์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์กฑ์Šค๋Ÿฝ์ง€ ๋ชปํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


ํ…Œ์ŠคํŠธ ์ž‘์„ฑ - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ๋งŽ์ด ์ œ๊ณตํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ๊ฑฐ์˜ ๋Œ€์œ„๋ฒ•์ด์ง€๋งŒ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์ž…๋ ฅ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.

์ข‹์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๋Ÿฐํƒ€์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋Ÿฐํƒ€์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ๋งŽ์ด ๋˜์ ธ์•ผ ํ•œ๋‹ค๋Š” ๋ง์ด ๋งž์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์ด ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ํŠน์ • ํŒŒ์ผ์—์„œ ์œ ํšจํ•œ ์ž…๋ ฅ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ค์ˆ˜๋กœ ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ์ œ๊ณตํ•˜๋ ค๋Š” ๊ฒฝ์šฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์œ ํ˜• ๊ฐ€๋“œ ๋˜๋Š” trust-me-operator( as ) ๋˜๋Š” ๊ฐ’ ๊ฐœ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๊ฐ€ ๋ฏธ๋ฆฌ ์‹คํŒจํ•  ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์•„๋Š” ๋Œ€์‹  ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. .

๋Ÿฐํƒ€์ž„ ํ…Œ์ŠคํŠธ์— trust-me-operator( as )๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ์šฉ๋„๋กœ๋งŒ ์˜ˆ์•ฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์œ ํšจํ•œ ์ž…๋ ฅ์„ ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ๋ฆฌํ„ฐ๋Ÿด์„ ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์— ํ• ๋‹นํ•˜๊ธฐ ์œ„ํ•ด ํ•ดํ‚น์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ฒƒ์ด ๋” ๋ช…ํ™• ํ•ฉ๋‹ˆ๋‹ค.

ํ–ฅํ›„์— ์ •๊ทœ์‹์„ ๋ณ€๊ฒฝํ•˜๋”๋ผ๋„ ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ ๋ฌธ์ œ๋กœ ์ธํ•ด ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋“ค์ด ํ…Œ์ŠคํŠธ์˜ ๋ชจ๋“  ๊ณณ์—์„œ ์šฐ๋ฆฌ๋ฅผ as ํ•œ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ๊นŒ์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž๊ฐ€ ์ž์‹ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ dogfoodํ•  ๋•Œ ๋ชจ๋“  ๊ณณ์—์„œ as ์‚ฌ์šฉํ•œ๋‹ค๋ฉด... ๋‹ค์šด์ŠคํŠธ๋ฆผ ์†Œ๋น„์ž๋Š” ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ทธ๋“ค์€ ๋˜ํ•œ ๋ชจ๋“  ๊ณณ์—์„œ as ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ ๋Ÿฐํƒ€์ž„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„๊นŒ์š”?

๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  ๊ณณ์—์„œ as ๋ฅผ ์‚ฌ์šฉํ•  ์ด์œ ๊ฐ€ ์ค„์–ด๋“ค๊ณ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž์™€ ๋‹ค์šด์ŠคํŠธ๋ฆผ ์†Œ๋น„์ž ๋ชจ๋‘ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊นจ๋Š” ๊ฒƒ์„ ๋” ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(์žฅํ™ฉํ•˜์ง€๋งŒ ๋‚ด ์š”์  ์ค‘ ์ผ๋ถ€๊ฐ€ ํ†ต๊ณผ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.)


๋˜ํ•œ ๋‚˜๋Š” ๋งŽ์€ ์ปดํŒŒ์ผ ํƒ€์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค(TS ํŒ€๋„ ๊ทธ๋ ‡๊ฒŒ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค).

ํŠน์ • string ๋ฆฌํ„ฐ๋Ÿด์ด ๋‚ด ์ปดํŒŒ์ผ ํƒ€์ž„ ํ…Œ์ŠคํŠธ์—์„œ ์ •๊ทœ์‹ ๊ฒ€์‚ฌ์— ์‹คํŒจ/ํ†ต๊ณผํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ๋กœ์„œ๋Š” ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์— ๋Œ€ํ•œ ์ปดํŒŒ์ผ ํƒ€์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๋Œ€์‹  ๋Ÿฐํƒ€์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์ด ๋‚ด ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ํ…Œ์ŠคํŠธ์— ์‹คํŒจ/ํ†ต๊ณผํ•˜๋ฉด ๋‹ค์šด์ŠคํŠธ๋ฆผ ์†Œ๋น„์ž๊ฐ€ ํ•ด๋‹น ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด(๋˜๋Š” ์œ ์‚ฌํ•œ ๋ฌธ์ž์—ด)์„ ์‚ฌ์šฉํ•˜๊ณ  ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ํ™•์‹ ์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


์ด๋Ÿฌ๋‹ค ๋ฐ”๋ฒจํƒ‘ ์‚ฌํƒœ๋กœ ์ˆœ์‹๊ฐ„์— ํ˜๋Ÿฌ๊ฐ€๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ...

์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์— ๋”์šฑ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์˜ˆ์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐ„/๋ฒ„์ „ ํ˜ธํ™˜์„ฑ์ด ๋”์ฐํ•ฉ๋‹ˆ๋‹ค...

๊ทธ๋Ÿฌ๋‚˜ regexes/string-pattern ์œ ํ˜•์€ ํ•ด๋‹น ๋ฌธ์ œ์— ๋น ์ง€์ง€ ์•Š์„ ์ ์ ˆํ•œ ๊ธฐํšŒ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค(ํ‘œ์ค€ํ™” ๋ฐ ์ •์ƒ์ ์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ž‘์„ฑ์ž ๋•๋ถ„์—).


ํŽธ์ง‘ํ•˜๋‹ค

์Šค๋ ˆ๋“œ์—์„œ ์ผ๋ฐ˜์ ์ธ ๊ฒƒ์€ ์ด๋Ÿฌํ•œ ์ •๊ทœ์‹์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์ฝ”๋“œ๊ฐ€ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹Œ ๋Ÿฐํƒ€์ž„ ์ œ๊ณต ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์‹คํ–‰๋˜๋”๋ผ๋„ ํ…Œ์ŠคํŠธ ๋ฌธ์ž์—ด์ด " ์˜ณ์€". ์ด๊ฒƒ์€ ๋Œ€์‹ ์— ๋ช…๋ชฉ/ํƒœ๊ทธ/๋ธŒ๋žœ๋“œ ๋ฌธ์ž์—ด์— ๋Œ€ํ•œ ์ธ์ˆ˜๋กœ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์„ ์–ด๋Š ์ชฝ์ด๋“  ์ž‘์„ฑํ•˜๊ณ  ํ…Œ์ŠคํŠธ์˜ ์ด์ ์€ ์ฒ ์ €ํ•˜๊ฒŒ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ ์ž…๋ ฅ์˜ ๋ชจ๋“  ์˜ค๋ฅ˜๋Š” ๊ฐœ๋ฐœ ์ฃผ๊ธฐ ์ดˆ๊ธฐ์— ํ”Œ๋ž˜๊ทธ ์ง€์ •).

์•„... ์ด ๊ธ€์„ ์“ฐ๊ธฐ ์ „์— ๋จผ์ € ๋‹ค ์ฝ์—ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ...

์–ด์จŒ๋“  ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์ด ์œ ์šฉํ•œ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.


HTTP ๊ฒฝ๋กœ ์„ ์–ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTTP ๊ฒฝ๋กœ ์„ ์–ธ ๊ฐœ์ฒด๋ฅผ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„ ์–ธ์€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๋ชจ๋‘์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

/*snip*/
createTestCard : f.route()
    .append("/platform")
    .appendParam(s.platform.platformId, /\d+/)
    .append("/stripe")
    .append("/test-card")
/*snip*/

์ด๊ฒƒ์€ .append() ๋Œ€ํ•œ ์ œ์•ฝ ์กฐ๊ฑด์ž…๋‹ˆ๋‹ค.

  • ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด๋งŒ ํ•ด๋‹น(์ง€๊ธˆ์€ ์ ์šฉํ•  ์ˆ˜ ์—†์ง€๋งŒ ๋น„ ๋ฆฌํ„ฐ๋Ÿด์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฝ๋กœ ์„ ์–ธ ๋นŒ๋”๊ฐ€ ์“ฐ๋ ˆ๊ธฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค)
  • ์„ ํ–‰ ์Šฌ๋ž˜์‹œ( / )๋กœ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ( / )๋กœ ๋๋‚˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • conlon ๋ฌธ์ž( : )๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜์šฉ์œผ๋กœ ์˜ˆ์•ฝ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šฌ๋ž˜์‹œ๋ฅผ ์—ฐ์†์ ์œผ๋กœ ํฌํ•จํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค( // ).

์ง€๊ธˆ์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋Ÿฐํƒ€์ž„ ๊ฒ€์‚ฌ๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์šด์ŠคํŠธ๋ฆผ ์†Œ๋น„์ž๊ฐ€ Github README ๋˜๋Š” JSDoc ์ฃผ์„์„ ์ฝ์„ ํ•„์š” ์—†์ด ์ด๋Ÿฌํ•œ ์ œ์•ฝ ์กฐ๊ฑด์„ ๋”ฐ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋นจ๊ฐ„์ƒ‰ ๊ตฌ๋ถˆ๊ตฌ๋ถˆํ•œ ์„ ์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.


๊ธฐํƒ€ ๋ฌผ๊ฑด

16์ง„์ˆ˜ ๋ฌธ์ž์—ด, ์˜์ˆซ์ž ๋ฌธ์ž์—ด์— ๋Œ€ํ•œ ์ •๊ทœ์‹๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋„ ์ด๊ฑฐ ์žˆ๋Š”๋ฐ,

const floatingPointRegex = /^([-+])?([0-9]*\.?[0-9]+)([eE]([-+])?([0-9]+))?$/;

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณธ๋‹ค,

์ •์ˆ˜ - "3e5"๋ฅผ ์ž˜๋ชป ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ ์ •์ˆ˜ ์ •๊ทœ์‹์ด ์•„๋‹ˆ์ง€๋งŒ floatingPointRegex .

function parseFloatingPointString (str : string) {
    const m = floatingPointRegex.exec(str);
    if (m == undefined) {
        return undefined;
    }
    const rawCoefficientSign : string|undefined = m[1];
    const rawCoefficientValue : string = m[2];
    const rawExponentSign : string|undefined = m[4];
    const rawExponentValue : string|undefined = m[5];

    const decimalPlaceIndex = rawCoefficientValue.indexOf(".");
    const fractionalLength = (decimalPlaceIndex < 0) ?
        0 :
        rawCoefficientValue.length - decimalPlaceIndex - 1;

    const exponentValue = (rawExponentValue == undefined) ?
        0 :
        parseInt(rawExponentValue) * ((rawExponentSign === "-") ? -1 : 1);

    const normalizedFractionalLength = (fractionalLength - exponentValue);
    const isInteger = (normalizedFractionalLength <= 0) ?
        true :
        /^0+$/.test(rawCoefficientValue.substring(
            rawCoefficientValue.length-normalizedFractionalLength,
            rawCoefficientValue.length
        ));
    const isNeg = (rawCoefficientSign === "-");

    return {
        isInteger,
        isNeg,
    };
}

์ด๋Ÿฐ ๋Œ“๊ธ€๋„ ์žˆ์ง€๋งŒ,

/**
    Just because a string is in integer format does not mean
    it is a finite number.

    ```ts
    const nines_80 = "99999999999999999999999999999999999999999999999999999999999999999999999999999999";
    const nines_320 = nines_80.repeat(4);
    //This will pass, 320 nines in a row is a valid integer format
    integerFormatString()("", nines_320);
    //Infinity
    parseFloat(nines_320);
    ```
*/

์ •๊ทœ ํ‘œํ˜„์‹ ์ƒ์„ฑ์ž

์žฌ๋ฏธ์žˆ๊ฒŒ๋„ RegExp ์ƒ์„ฑ์ž๋Š” ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆ๋œ ๋ฌธ์ž์—ด ์œ ํ˜•์˜ ์ด์ ์„ ๋ˆ„๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค!

๋ฐ”๋กœ ์ง€๊ธˆ, ๊ทธ๊ฒƒ์€,

new(pattern: string, flags?: string): RegExp

ํ•˜์ง€๋งŒ,

new(pattern: string, flags?: PatternOf</^[gimsuy]*$/>): RegExp

TL;DR (๊ผญ ์ฝ์–ด๋ณด์„ธ์š”, ์ œ๊ฐ€ ๊ณต์„ ๋งŽ์ด ๋“ค์˜€์Šต๋‹ˆ๋‹ค :cry: )

  • ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์€ ๊ณต์นญ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•๋ณด๋‹ค ์ธ์ฒด๊ณตํ•™์ ์ž…๋‹ˆ๋‹ค.

    • ๋œ ์ƒ์šฉ๊ตฌ

  • ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์€ ๋ช…๋ชฉ/๊ตฌ์กฐ ํƒœ๊ทธ ์œ ํ˜•๋ณด๋‹ค ๋ฐ”๋ฒจํƒ‘ ์ƒํ™ฉ์ด ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์ ์Šต๋‹ˆ๋‹ค.

    • ํŠนํžˆ ์ •๊ทœ์‹ ํ•˜์œ„ ์œ ํ˜• ๊ฒ€์‚ฌ์˜ ๊ฒฝ์šฐ

  • ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์€ string ์œ ํ˜•์˜ ๋Œ€๊ทœ๋ชจ ์œ ํ•œ/๋ฌดํ•œ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ์ •์˜ํ•˜๋Š” ๊ฐ€์žฅ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

    • ์ด ๊ธฐ๋Šฅ์„ ๋„์ž…ํ•˜๋ฉด ์‚ฌ๋žŒ๋“ค์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์œ ํšจํ•œ ๋ฌธ์ž์—ด ํ˜•์‹์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์ƒ๊ฐํ•˜๊ฒŒ ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค!

  • ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜• (๋–จ์–ด์ ธ ... ๋‚˜ ์œ ํ–‰์— ๋‹ค์‹œ ์—ฐ๋ฝํ•˜์ž

    • RegExp ์ƒ์„ฑ์ž, 16์ง„์ˆ˜/์˜์ˆซ์ž ๋ฌธ์ž์—ด, ๊ฒฝ๋กœ ๊ฒฝ๋กœ ์„ ์–ธ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์šฉ ๋ฌธ์ž์—ด ์‹๋ณ„์ž ๋“ฑ


์ •๊ทœ์‹์€ ์™œ ๊ทธ๋ ‡๊ฒŒ ๋‚˜์ฉ๋‹ˆ๊นŒ?

๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ œ๊ธฐํ•œ ๋งŽ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๊ธฐ์กด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋งž๊ฒŒ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์„ ๋„์ž…ํ•˜๊ธฐ๋ฅผ ์›ํ–ˆ์Šต๋‹ˆ๋‹ค. TS ํŒ€์„ ์„ค๋“ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ข…์ข… ์ด๋Ÿฌํ•œ ๊ธฐ์กด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ž…๋ ฅ์„ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ์ •๊ทœ์‹์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋˜๋Š” ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋” ๋ณต์žกํ•œ ํŒŒ์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์ œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์— ๋Œ€ํ•œ ์‹ค์ œ ์œ ํšจํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค!


์œ ํšจํ•œ ๋ฌธ์ž์—ด ๊ฐ’์˜ ์ƒ์œ„ ์ง‘ํ•ฉ์„ ํ™•์ธํ•˜๋Š” ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•

๋ฌผ๋ก  / ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์ž์—ด์€ / ๋กœ ๋๋‚˜์ง€ ์•Š๊ณ  ์—ฐ์†์ ์ธ / ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉฐ : ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๋ฌธ์ž์—ด์€ " HTTP ๊ฒฝ๋กœ ์ •๊ทœ์‹". ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ด ์ •๊ทœ์‹์„ ์ „๋‹ฌํ•˜๋Š” ๊ฐ’ ์ง‘ํ•ฉ ์ด ์œ ํšจํ•œ HTTP ๊ฒฝ๋กœ์˜

๋” ์•„๋ž˜๋กœ ? ๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋Š”์ง€, # ๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋Š”์ง€, ์ผ๋ถ€ ๋ฌธ์ž๊ฐ€ ์ด์Šค์ผ€์ดํ”„๋˜์—ˆ๋Š”์ง€ ๋“ฑ์„ ํ™•์ธํ•˜๋Š” ์‹ค์ œ URL ๊ฒฝ๋กœ ํŒŒ์„œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ฐ„๋‹จํ•œ ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์œผ๋กœ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž๊ฐ€ ์ง๋ฉดํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์˜ ํฐ ๋ถ€๋ฅ˜๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค! ๊ทธ๋ฆฌ๊ณ  ์ปดํŒŒ์ผ ํƒ€์ž„์—๋„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค!

๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ์ž๊ฐ€ ? ๊ฐ€ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ ๋งŒํผ ๊ฒฝํ—˜์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ HTTP ๊ฒฝ๋กœ์—์„œ ? ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งŽ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


๋‚˜๋Š” ๋‹น์‹ ์ด ์ด๋ฏธ ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

์ด ์Šค๋ ˆ๋“œ๋Š” ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์ธ ์˜ˆ๋Š” ๋” ๋“œ๋ฌผ์—ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์ด๋Ÿฌํ•œ ์˜ˆ์ œ ์ค‘ ๋งŽ์€ ๋ถ€๋ถ„์ด ์™„์ „ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ํšจํ•œ ์ž…๋ ฅ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” RegExp๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ œ์•ˆ๋œ ๋งŽ์€ ์ •๊ทœ์‹์€ "์™„๋ฒฝ"ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์œ ํšจํ•œ ์ž…๋ ฅ์„ ๊ฑฐ๋ถ€ํ•˜์ง€ ์•Š๋Š” ํ•œ ๊ดœ์ฐฎ๊ฒ ์ฃ ?

์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ํ—ˆ์šฉํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ฃ ?
๋Ÿฐํƒ€์ž„ ๋™์•ˆ "์‹ค์ œ" ํŒŒ์„œ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ๊ฒ€์‚ฌ๋Š” ๋‹ค์šด์ŠคํŠธ๋ฆผ ์‚ฌ์šฉ์ž์˜ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ๋ฅผ ๋งŽ์ด ์ œ๊ฑฐํ•˜์—ฌ ์ƒ์‚ฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ ํšจํ•œ ์ž…๋ ฅ์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ์˜ˆ์ œ๋Š” ์ˆ˜์ •ํ•˜๊ธฐ ์‰ฌ์›Œ์•ผ ์œ ํšจํ•œ ์ž…๋ ฅ์„ ๊ฑฐ๋ถ€ํ•˜์ง€ ์•Š์ง€๋งŒ ์ž˜๋ชป๋œ ์ž…๋ ฅ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.


๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜• ๋ฐ ๊ต์ฐจ

์–ด์จŒ๋“  ๋ฌธ์ž์—ด ํŒจํ„ด ์œ ํ˜•์˜ ๊ต์ฐจ ์œ ํ˜•์€ ๋งค์šฐ ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค!

๋‚ด .append() ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

append (str : (
  //Must start with forward slash
  & PatternOf</^\//>
  //Must not end with forward slash
  & PatternOf</[^/]$/>
  //Must not have consecutive forward slashes anywhere
  & not PatternOf</\/\//>
  //Must not contain colon
  & PatternOf</^[^:]+$/>
)) : SomeReturnType;

not PatternOf</\/\//> ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
PatternOf</^((([/])(?!\3))|[^/])+$/> ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ํ›จ์”ฌ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

๊ด‘๋ฒ”์œ„ํ•œ ๋ฐ๋ชจ์— ๋Œ€ํ•ด @AnyhowStep ์—๊ฒŒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ฑ…์„ ๋„ˆ๋ฌด ๋งŽ์ด ์ฝ๊ฒŒ ํ•ด์„œ ๋น„๋‚œํ•˜๊ณ  ์‹ถ์—ˆ์ง€๋งŒ ๊ฒฐ๊ณผ์ ์œผ๋กœ๋Š” ๋งŽ์€ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

๋‚˜๋Š” ์ข…์ข… ๋ฌธ์ž์—ด ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ€๋“ ์ฐฌ ๋‚ด๋ถ€ API๋ฅผ ์ž…๋ ฅํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์œผ๋ฉฐ ๋ถˆ๊ฐ€ํ”ผํ•˜๊ฒŒ ๋Ÿฐํƒ€์ž„์— ๋ฐœ์ƒํ•˜๋Š” ๋งŽ์€ ์กฐ๊ฑด๋ฌธ์œผ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค. ๋ถˆ๊ฐ€ํ”ผํ•˜๊ฒŒ ๋‚ด ์†Œ๋น„์ž๋Š” ์˜ˆ์™ธ๋ฅผ ์›ํ•˜์ง€ ์•Š๊ณ  ์‹คํŒจ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํŠน๋ณ„ํ•œ ๋ฐฉ๋ฒ•์„ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ํŒจํ„ด ๊ฒ€์‚ฌ๋ฅผ ๋ณต์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// Today
function createServer(id: string, comment: string) {
  if (id.match(/^[a-z]+-[0-9]+$/)) throw new Error("Server id does not match the format");
  // work
}

// Nicer
function createServer(id: PatternOf</^[a-z]+-[0-9]+$/>, comment: string) {
  // work immediately
}

๋ฌธ์ž์—ด๊ณผ ํŒจํ„ด์˜ ์„ธ๊ณ„์—์„œ ์ผ๋ฐ˜ string ์€ unknown ์™€ ๊ฑฐ์˜ ๋™์ผํ•˜๋ฉฐ ๋Ÿฐํƒ€์ž„ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•ด ๋งŽ์€ ์œ ํ˜• ์•ˆ์ „์„ ์ œ๊ฑฐํ•˜๊ณ  ์†Œ๋น„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋ถˆํŽธ์„ ์ค๋‹ˆ๋‹ค.

์–ธ๊ธ‰๋œ ์ผ๋ถ€ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ๊ฒฝ์šฐ Regex์˜ ์ž‘์€ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ ‘๋‘์‚ฌ ์ผ์น˜).

์ž ์žฌ์ ์œผ๋กœ ์ด๊ฒƒ์€ Variadic Kinds #5453 ๋ฐ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์„ ํ™•์‚ฐํ•  ๋•Œ ์œ ํ˜• ์œ ์ถ”์™€ ๊ฐ™์€ ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ TS ์–ธ์–ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฏธ๋ž˜์˜ ์ถ”์ธก:

const x: ['a', 'b', 'c'] = [...'abc'] as const;

type T = [...'def']; // ['d', 'e', 'f'];
type Guard<T extends string> =
  [...T] extends [...'https://', ...any[]] ? Promise<any> : never;

declare function secureGET<
  T extends string
>(url: T): Guard<T>;

const x = secureGET('https://a.com');
x.then(...) // okay

const z = secureGET('http://z.com');
z.then(...); // error
type NaturalNumberString<T extends string> =
  [...T] extends ('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9')[] ? T : never;

์–ธ๊ธ‰๋œ ์ผ๋ถ€ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ๊ฒฝ์šฐ Regex์˜ ์ž‘์€ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ ‘๋‘์‚ฌ ์ผ์น˜).

๋‚˜๋Š” ์—ฌ์ „ํžˆ ์ด + ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ์ œ๊ณตํ•˜๋Š” ๋‚ด ์ œ์•ˆ ์„ ์ง€์ง€ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณ„์ด ์—†๋Š” ์–ธ์–ด ์˜ ์•„์ฃผ ์ž‘์€ ์ƒ์œ„ ์ง‘ํ•ฉ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ํ•ฉ๋ฆฌ์ ์œผ๋กœ ํšจ์œจ์ ์œผ๋กœ ๋ถ€๋ถ„ ์ง‘ํ•ฉ๊ณผ ํ‰๋“ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ๊นŒ์ง€ TS ํŒ€์˜ ๊ฐ€์žฅ ํฐ ์šฐ๋ ค์ธ ์ž„์˜ ์ •๊ทœ์‹์˜ ์„ฑ๋Šฅ ์ธก๋ฉด์„ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋‹ค๋ฅธ ์ œ์•ˆ์„ ๋ณธ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค.

๋ณ„ํ‘œ ์—†๋Š” ์–ธ์–ด์˜ ๋ฌธ์ œ๋Š” ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด ๋ณ„ํ‘œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— URL๊ณผ ๊ฐ™์€ ํ•ญ๋ชฉ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์€ ๋ณ„์„ ์›ํ•  ๊ฒƒ์ด๊ณ  ์ž„์˜์˜ ์ˆ˜์˜ ๋ฐ˜๋ณต ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ„์„ ์—๋ฎฌ๋ ˆ์ดํŠธํ•˜๋ฏ€๋กœ ํ•˜์œ„ ์ง‘ํ•ฉ์„ ํ™•์ธํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋Œ€๋ถ€๋ถ„์˜ ์ผ๋ฐ˜ DFA ํ‘œํ˜„ ๊ฐ€๋Šฅ ์ •๊ทœ์‹์˜ ์„ฑ๋Šฅ์€ ๊ทธ๋ ‡๊ฒŒ ๋‚˜์˜์ง€ ์•Š์œผ๋ฉฐ ํ•˜์œ„/์ƒ์œ„ ์ง‘ํ•ฉ์— ๋Œ€ํ•ด ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜๋„ ์ผ์ข…์˜ * ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const str : PatternOf</ab+c/> | PatternOf</ac/>

@TijmenW ๋‚ด ์ œ์•ˆ์„ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์ฝ์œผ์‹ญ์‹œ์˜ค. ์ˆจ๊ฒจ์ง„ ๊ทผ๊ฑฐ์™€ ์‹ค์ œ๋กœ ์‹ค์šฉ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ช‡ ๊ฐ€์ง€ ์ž‘์€ ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ„ํ‘œ ์—†๋Š” ๋ฌธ๋ฒ•์„ ์ง€์ •ํ•˜๋Š” ๋ฐ ์ง์ ‘์ ์œผ๋กœ ์ œํ•œ๋˜์ง€๋Š” ์•Š์ง€๋งŒ ๋‚ด ์ค€๊ณ ๊ธ‰ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ์‹ค์งˆ์ ์œผ๋กœ ์œ ์šฉํ•  ๋งŒํผ๋งŒ ํ™•์žฅ๋œ ์ž‘์€ ์ƒ์œ„ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ, ๊ฐœ๋ณ„ ๋ฌธ์ž์— ๋Œ€ํ•ด starof ('a' | 'b' | ...) ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ณ  string ์™€ ๋™๋“ฑํ•˜๊ฒŒ starof UnionOfAllCodePoints (์‚ฌ์‹ค์ƒ ์ด๋ก ์ƒ ๋” ์ด์ƒ ๊ธฐ๋ณธ ์š”์†Œ๊ฐ€ ์•„๋‹˜).

๋˜ํ•œ ์ •๊ทœ ์–ธ์–ด๊ฐ€ ๋‹ค๋ฅธ ์ •๊ทœ ์–ธ์–ด์™€ ์ผ์น˜ํ•˜๋Š” ๋ถ€๋ถ„ ์ง‘ํ•ฉ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ NP-์™„์ „ํ•˜๊ณ  ์ผ๋ฐ˜ ํ•˜์œ„ ๊ทธ๋ž˜ํ”„ ๋™ํ˜• ๋ฌธ์ œ ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ ํ‘œ์ค€ ์ •๊ทœ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ด์œ ์ด๋ฉฐ, ์ด๋ก ์  ๊ณ„์‚ฐ ๋ณต์žก์„ฑ์„ ๋‚ฎ์ถ”๊ธฐ ์œ„ํ•ด ๊ฐ€๋Šฅํ•œ ํ•œ starof ๋ฅผ ์ œํ•œํ•˜๋ ค๊ณ  ํ•œ ์ด์œ ์ž…๋‹ˆ๋‹ค.

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

์ด๊ฒƒ์€ ์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„์˜ ์ •๋ณด๋กœ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์žˆ์ง€๋งŒ https://github.com/ostrrowr/ts-json-validator ์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ •๊ทœ์‹ ์œ ํ˜•๊ณผ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ํ›จ์”ฌ ๋” ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ชฉํ‘œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ Typescript ์œ ํ˜•/JSON ์Šคํ‚ค๋งˆ ์Œ <T, s> ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  1. ๋ชจ๋“  ์œ ํ˜•์˜ ๊ฒƒ์„ s ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ํ• ๋‹น ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค T
  2. T ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ ์ ์€ ์œ ํ˜•์ด s ์— ๋Œ€ํ•ด ์‹คํ–‰๋  ๋•Œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์— ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์ •๊ทœ์‹ ์œ ํ˜•์€ ๊ฒ€์ฆ๋œ ์œ ํ˜•์ด ์ตœ์†Œํ•œ ๋‹ค์Œ ํ‚ค์›Œ๋“œ์— ๋Œ€ํ•ด ๋” ์—„๊ฒฉํ•˜๋„๋ก ํ—ˆ์šฉํ•˜์—ฌ (2)์˜ ์—„๊ฒฉ์„ฑ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

  • format
  • patternProperties
  • propertyNames

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

๋ชจ๋“  Excel ์ธํ„ฐํŽ˜์ด์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” A1 ๋˜๋Š” A5:B7 ํ˜•์‹ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์†์„ฑ ํ‚ค / ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์ธ๋ฑ์„œ

์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์†์„ฑ ์ด๋ฆ„์— ๋”ฐ๋ผ ๊ฐœ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด React์—์„œ ์ด๋ฆ„์ด aria- ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ์†Œํ’ˆ์— ์œ ํ˜•์„ ์ ์šฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

interface IntrinsicElements {
    // ....
    [attributeName: /aria-\w+/]: number | string | boolean;
}

์ด๊ฒƒ์€ ์‚ฌ์‹ค์ƒ ์ง๊ต ๊ฐœ๋…์ž…๋‹ˆ๋‹ค(Regex ์†์„ฑ ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  Regex ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ทธ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค).

๋‚˜๋Š” ์ด๊ฒƒ์ด ์—ฌ๊ธฐ์„œ ์ง„ํ–‰๋˜๋Š” ๋ชจ๋“  ๊ฒƒ๊ณผ ์•ฝ๊ฐ„ ์ง๊ตํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ Wesley๋Š” ๋‹น์‹ ์ด ์šฐ๋ฆฌ์˜ ์ž…๋ ฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ด์œ ๋กœ Fabric์—์„œ ๊ณ„์† ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ์„œ ์šฐ๋ฆฌ๋Š” data- ๋ฐ aria- ์†์„ฑ์„ ํฌํ•จํ•˜์—ฌ TypeScript์—์„œ ํ—ˆ์šฉํ•˜๋Š” React ์ปดํฌ๋„ŒํŠธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๋ฐ˜์˜ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ props ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ ์—†์ด๋Š” ์ด๋Ÿฌํ•œ ์†์„ฑ์— ์‚ฌ์šฉํ•  ์†Œ๋น„์ž ์—๊ฒŒ

์šฐ๋ฆฌ๊ฐ€ ๋„์šธ ์ˆ˜ ์žˆ๋Š” ์ผ์ด ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”! ๐Ÿ˜„

TS ๋†€์ดํ„ฐ :

import * as React from 'react';

// Want to reflect the same aria- and data- attributes here that JSX compiler allows in this interface:
interface TestComponentProps {
    someProp?: number;
}

const TestComponent: React.FunctionComponent<TestComponentProps> = () => {
    return null;
}

const ConsumerComponent: React.FunctionComponent = () => {
    // The React component interface allows for 'data-' and 'aria-' attributes, but we don't have any typesafe way of
    // elevating that interface or instantiating props objects that allow the same attributes. We just want to be able to 
    // define component interfaces that match what the React component interface allows without opening it up to 'any' and 
    // giving up all type safety on that interface.
    const testComponentProps: TestComponentProps = {
        someProp: 42,
        'data-attribute-allowed': 'test'
    };

    return (
        <TestComponent
            someProp={42}
            // 'data-' and 'aria-' attributes are only allowed here:
            data-attribute-allowed={'data-value'}
            aria-attribute-allowed={'aria-value'}
            {...testComponentProps}
        />
    )
}

TODO: RegExp ์œ ํ˜•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์‹ค์ œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•จ์ˆ˜์™€ ์‚ฌ์šฉํ•  ์‹ค์ œ ํ‘œํ˜„์‹์„ ์‹๋ณ„ํ•˜์—ฌ ์ €ํฌ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.

ํฌ๋ก  ์ž‘์—…. (์ด๊ฒƒ์€ ์–ธ๊ธ‰๋˜์ง€ ์•Š์€ ๊ฒƒ์— ๋งค์šฐ ๋†€๋ž์Šต๋‹ˆ๋‹ค)

^((\*|\d+((\/|\-|,){0,1}(\d+))*)\s*){6}$

์—ฌ๊ธฐ์— 2์„ผํŠธ๋งŒ ํˆฌ์žํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ €๋Š” HTML id ์†์„ฑ์œผ๋กœ ์‚ฌ์šฉ๋  ์†Œํ’ˆ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋ ค๋Š” React ํ”„๋กœ์ ํŠธ์—์„œ ์ž‘์—…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ ๊ทœ์น™์„ ์ถฉ์กฑํ•ด์•ผ ํ•˜๋ฉฐ ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  1. ํ•˜๋‚˜ ์ด์ƒ์˜ ์บ๋ฆญํ„ฐ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  2. ๊ณต๋ฐฑ ์—†์Œ

๋‹ค์‹œ ๋งํ•ด:

interface Props {
  id: PatternOf</[^ ]+/>;
}

๋˜ ๋‹ค๋ฅธ ์˜ˆ: '<namespace>/<name>[@<version>]' ํ˜•์‹์œผ๋กœ ์˜ˆ์ƒ๋˜๋Š” ๋ฌธ์ž์—ด์ด ์žˆ๋Š” ์„ฑ์—ญ ์œ ํ˜• ์‹๋ณ„์ž

์‚ฌ์šฉ ์‚ฌ๋ก€: Navigator.registerProtocolHandler() ์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด ํ˜•์‹ DOM API

MDN ์ธ์šฉ:

๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ registerProtocolHandler() ๋Š” ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ์ฒด๊ณ„๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ง€์ • ์ฒด๊ณ„๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑํ‘œ์˜ ์ด๋ฆ„์€ web+
  • ๋งž์ถค ๊ตฌ์„ฑํ‘œ์˜ ์ด๋ฆ„์—๋Š” web+ ์ ‘๋‘์‚ฌ ๋’ค์— ํ•˜๋‚˜ ์ด์ƒ์˜ ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ •์˜ ๊ตฌ์„ฑํ‘œ๋Š” ์ด๋ฆ„์— ์†Œ๋ฌธ์ž ASCII ๋ฌธ์ž๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, Navigator.registerProtocolHandler() ๋Š” ์ž˜ ์•Œ๋ ค์ง„ string ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ string ์˜ˆ์ƒํ•˜์ง€๋งŒ ํŠน์ • ์Šคํ‚ค๋งˆ๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

CSSType ์— ๋Œ€ํ•œ CSS ์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ์€ -- ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™์€ ์†์„ฑ์„ ์ œ์™ธํ•œ ๋ชจ๋“  ์†์„ฑ์— ๋Œ€ํ•ด ๋‹ซํžŒ ์œ ํ˜•์„ ์ œ๊ณตํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

interface Properties {
    // ....
    [customProperty: /--[a-z][^\s]*/]: number | string;
}`

๊ด€๋ จ https://github.com/frenic/csstype/issues/63

์ด๊ฒƒ์ด ๊ตฌ์ฒดํ™” ์œ ํ˜•๊ณผ ๋™์ผํ•œ์ง€ ๋ˆ„๊ตฐ๊ฐ€ ๋งํ•ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? https://github.com/microsoft/TypeScript/issues/7599

@gautam1168 ์ด๋ก ์ ์œผ๋กœ ๋ฌธ์ž์—ด ์œ ํ˜•์„ ๊ตฌ์ฒด์ ์œผ๋กœ ๊ตฌ์ฒดํ™”ํ•˜๋Š” ๋ถ€๋ถ„ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. (๋ฌผ๋ก  ์ˆซ์ž ์œ ํ˜•์—๋Š” ๊ณ ์œ ํ•œ ๊ด€์‹ฌ์‚ฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.)

์–ธ๊ธ‰๋œ ์ผ๋ถ€ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ๊ฒฝ์šฐ Regex์˜ ์ž‘์€ ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ์ ‘๋‘์‚ฌ ์ผ์น˜).

๋‚˜๋Š” ์—ฌ์ „ํžˆ ์ด + ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ๊ฒƒ๋“ค์„ ์ œ๊ณตํ•˜๋Š” ๋‚ด ์ œ์•ˆ ์„ ์ง€์ง€ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณ„์ด ์—†๋Š” ์–ธ์–ด ์˜ ์•„์ฃผ ์ž‘์€ ์ƒ์œ„ ์ง‘ํ•ฉ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ํ•ฉ๋ฆฌ์ ์œผ๋กœ ํšจ์œจ์ ์œผ๋กœ ๋ถ€๋ถ„ ์ง‘ํ•ฉ๊ณผ ํ‰๋“ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ๊นŒ์ง€ TS ํŒ€์˜ ๊ฐ€์žฅ ํฐ ์šฐ๋ ค์ธ ์ž„์˜ ์ •๊ทœ์‹์˜ ์„ฑ๋Šฅ ์ธก๋ฉด์„ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋‹ค๋ฅธ ์ œ์•ˆ์„ ๋ณธ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค.

์ด ๋Œ“๊ธ€์—์„œ,
https://github.com/microsoft/TypeScript/issues/6579#issuecomment -243338433

์—ฐ๊ฒฐ๋œ ๋ˆ„๊ตฐ๊ฐ€,
https://bora.uib.no/handle/1956/3956

์ œ๋ชฉ "์ •๊ทœ ํ‘œํ˜„์‹์˜ ํฌํ•จ ๋ฌธ์ œ"


ํ•˜์ง€๋งŒ,

  • ์˜ค๋ฅธ์ชฝ ํ‘œํ˜„์‹์ด 1-๋ชจํ˜ธํ•˜์ง€ ์•Š์œผ๋ฉด ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์ •๋‹ต์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ •๋‹ต์ด ๋‚˜์˜ค๊ฑฐ๋‚˜ ๋‹ต์ด ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://www.sciencedirect.com/science/article/pii/S0022000011001486

(๋ฌผ๋ก  JS ์ •๊ทœ์‹์€ ๋น„์ •๊ทœ์‹์ด๋‹ค)

@AnyhowStep ํšจ๊ณผ ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. starof ์ œํ•œ์„ ํ•ด์ œํ•˜๊ณ  ๊ทธ์— ๋”ฐ๋ผ ์ œํ•œ์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜ํ•™์ด ์กฐ๊ธˆ ์ถ”์ƒ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๊ฐ€ ์ œํ•œ์„ ํŠน์„ฑํ™”ํ•˜๋Š” ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ๋ถ€ํƒํ•˜๊ณ  ๊ตฌ์ฒด์ ์œผ๋กœ ์‹ค์ฒœ์— ์ ์šฉ ํ•  ๋ฐฉ์‹์ด ๋ถˆ๋ถ„๋ช… ํ•œ (๊ทธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋ชจ๋‘๊ฐ€ ๊ณต์‹ ์–ธ์–ด๋กœ ์ž˜ ์ •ํ†ตํ•œ ์žˆ์Šต๋‹ˆ๋‹ค).

๋˜ํ•œ ๋ณ„๋„๋กœ starof ๋Œ€ํ•œ ๋” ๋‚˜์€ ๋Œ€์•ˆ์„ ์—ฐ์‚ฐ์ž๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๋Ÿฐ ์ข…๋ฅ˜์˜ ๊ฒƒ์„ ๋ชจ๋ธ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ •๊ทœ์‹์˜ ํฌํ•จ/ํฌํ•จ์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? Wikipedia์— ๋”ฐ๋ฅด๋ฉด ๊ฒฐ์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด JS์˜ ์ •๊ทœ ํ‘œํ˜„์‹๋„ ์„ค๋ช…ํ•ฉ๋‹ˆ๊นŒ? ํ‘œ์ค€ RE(์˜ˆ: ์—ญ์ฐธ์กฐ)๋ณด๋‹ค ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ์ • ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๊ณ„์‚ฐ์ ์œผ๋กœ ์‹คํ˜„ ๊ฐ€๋Šฅํ•œ๊ฐ€?
์ด๊ฒƒ์€ ์ด ๊ธฐ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค(์ขํžˆ๊ธฐ):

if (Gmail.test(candidate)) {
    // candidate is also an Email
}

@nikeee Decidable์€ ์ด๊ฒƒ์ด ํ˜„์‹ค์ ์ด๊ธฐ์— ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 2์ฐจ ์‹œ๊ฐ„์กฐ์ฐจ๋„ ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ๊ทœ๋ชจ์—์„œ ๋„ˆ๋ฌด ๋Š๋ฆฝ๋‹ˆ๋‹ค. TS๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋น„์Šทํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋ฐฐ๊ฒฝ ์ง€์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์—ญ ์ฐธ์กฐ์— ์ง๋ฉดํ•˜์—ฌ ์—ฌ์ „ํžˆ ๊ฒฐ์ • ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋” ๋‚˜์˜์ง€๋Š” ์•Š๋”๋ผ๋„ ๊ธฐํ•˜ ๊ธ‰์ˆ˜์ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ๊ต์œก๋ฐ›์€ ์ถ”์ธก์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ๋ช…ํ™•ํžˆ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

2์ฐจ ์‹œ๊ฐ„์กฐ์ฐจ๋„ ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ๊ทœ๋ชจ์—์„œ ๋„ˆ๋ฌด ๋Š๋ฆฝ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋‚ด๊ฐ€ ๊ณ„์‚ฐ์ ์œผ๋กœ ์‹คํ˜„ ๊ฐ€๋Šฅํ•œ์ง€ ๋ฌผ์€ ์ด์œ ์ด๊ธฐ๋„ ํ•˜๊ณ , ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

ํ‰๋“ฑ์—๋„ ๋™์ผํ•˜๊ฒŒ ์ ์šฉ๋œ๋‹ค๋ฉด ์ด ๊ธฐ๋Šฅ์˜ ๊ฑฐ์˜ ๋ชจ๋“  ์†์„ฑ์ด ์‹คํ–‰ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ์ œ๊ฐ€ ํ‹€๋ ธ๋‹ค๋ฉด ์ •์ •ํ•ฉ๋‹ˆ๋‹ค๋งŒ, ๋‚จ์€ ๊ฒƒ์€ ํšŒ์›๊ฐ€์ž…๋ฟ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์œ ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™์•„์š”.

@nikeee ์ด ํŒจํ„ด์€ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ๋น„๊ต ๋Œ€์ƒ์ด ๋˜๋Š” ๋ชจ๋“  ์œ ํ˜•์˜ ๋ชจ๋“  ์†์„ฑ์— ๋Œ€ํ•ด ๊ฒ€์‚ฌ๋œ๋‹ค๋Š” ์ ์„ ์—ผ๋‘์— ๋‘˜ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  regexp ์†์„ฑ์ด ์žˆ๋Š” ์œ ํ˜•์˜ ๊ฒฝ์šฐ regexp๊ฐ€ ๊ทธ ์ž์ฒด๋กœ ๋‹ค์†Œ ๋ณต์žกํ•œ ์ง์Šน์ธ ๋‹ค๋ฅธ regexp์™€ ์ผ์น˜ํ•˜๋Š” ํ•˜์œ„ ์ง‘ํ•ฉ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๊ณ„์‚ฐ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์–ด๋ ค์šธ ๋ฟ์ด๋ฉฐ ์‹คํ˜„ ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด ์ œํ•œ์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ๋ฅผ ๋“ค์–ด, JS regexps๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ™•์žฅ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋„ˆ๋ฌด ์œ ์—ฐํ•ฉ๋‹ˆ๋‹ค.)

ํŽธ์ง‘: ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋˜ํ’€์ดํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค: ์ €๋Š” ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ ์œ„ํ•ด TS ํŒ€์— ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค . CS ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ค๊ณ„์— ๋Œ€ํ•œ ๋ฐฐ๊ฒฝ ์ง€์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ , ๊ทธ๋ž˜์„œ "์ผ๋ฐ˜์ ์ธ" RegEx์˜ "์ œํ•œ๋œ" ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๊ฐ์•ˆํ•  ๋•Œ ์ •๊ทœ์‹์€ ์ง€๊ธˆ๊นŒ์ง€ ๋งค์šฐ ๊ฐ„๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. (์ƒ‰์ƒ, ์ „ํ™” ๋ฒˆํ˜ธ ๋“ฑ)

ํ•˜์œ„ ์ง‘ํ•ฉ๋งŒ ์ง€์›ํ•˜๋Š” UX๋ฅผ ์–ด๋–ป๊ฒŒ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? RegEx์˜ ๊ธฐ๋Šฅ X๋Š” ์ž‘๋™ํ•˜์ง€๋งŒ Y๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ช…ํ™•ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Œ ... "์ •๊ทœ์‹"์ด๋ผ๊ณ  ๋ถ€๋ฅด์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ์šฐ์„  ์‹œ์ž‘ํ•˜์‹ญ์‹œ์˜ค. ์•„๋งˆ๋„ "ํŒจํ„ด ์ผ์น˜" ์ •๋„:see_no_ evil:. ํ•˜์ง€๋งŒ ๋„ค, ์•„๋งˆ๋„ ์‰ฌ์šด ์ผ์ด ์•„๋‹ ๊ฒƒ์ž…๋‹ˆ๋‹ค...

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋น„์ •๊ทœ์‹ ๊ตฌ๋ฌธ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type URL = `${'http'|'https'}://${Domain}`;

const good: URL = 'https://google.com'; // โœ”๏ธ
const bad: URL = 'ftp://example.com'; // โœ–๏ธ TypeError: 'ftp' is not assignable to type 'http' | 'https'

๋‚ด ์ƒ๊ฐ์— ์ด๊ฒƒ์€ ์œ ํ˜• ์‹œ์Šคํ…œ์— ์•„์ฃผ ์ž˜ ๋งž์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์„ ํƒ์  ์ผ์น˜์™€ ๊ฐ™์€ ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type SubDomain = `${string}.`;
type Domain = `${SubDomain}?${string}.${TLD}`;

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

์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋” ์‚ฌ์šฉ์ž ์นœํ™”์ ์ผ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ ํ˜•์— ๋Œ€ํ•œ ์‚ฐ์ˆ  ์—ฐ์‚ฐ์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
https://github.com/microsoft/TypeScript/issues/15645#issuecomment -299917814 ๋ฐ https://github.com/microsoft/TypeScript/issues/15794#issuecomment -301170109์— ๋”ฐ๋ฅด๋ฉด ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋””์ž์ธ ๊ฒฐ์ •์ž…๋‹ˆ๋‹ค. ์œ ํ˜•์— ๋Œ€ํ•œ ์‚ฐ์ˆ .
๋‚ด๊ฐ€ ์ฐฉ๊ฐํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋ฉด ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ์—„์ฒญ๋‚œ ์œ ํ˜•์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๋ คํ•˜๋‹ค:

type TLD = 'com' | 'net' | 'org' | 'ly' | 'a' | 'b' | 'c' | 'd';
type Foo = `${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}`;
type Bar = `${Foo}${Foo}${Foo}${Foo}${Foo}`

(์ด๊ฒƒ์€ ๊ตฌํ˜„์—์„œ ๊ณต์šฉ์ฒด ์œ ํ˜•์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ/๋” ๋ณต์žกํ•œ ๊ตฌํ˜„์—์„œ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Œ)

๋ฉด์ฑ… ์กฐํ•ญ: ์ €๋Š” TS ํŒ€์˜ ์ผ์›์ด ์•„๋‹ˆ๋ฉฐ TS์—์„œ ์ผํ•˜๊ณ  ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๋‚ด 2c.

@rozzzly @nikeee ๋ช‡ ๊ฐ€์ง€ ์ž‘์€ ๊ธฐ๋Šฅ์ด ๋ˆ„๋ฝ๋œ ๊ฒƒ์ด ์ œ ์ œ์•ˆ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค . ๋‚˜๋Š” ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด ๋“ฑ์˜ ์˜๋ฏธ์—์„œ ์ •๊ทœ์‹์ด ์•„๋‹Œ ์ •๊ทœ ์–ธ์–ด์˜ ํฐ ๋ถ€๋ถ„ ์ง‘ํ•ฉ(๊ณต์‹ ์–ธ์–ด ๊ฐœ๋…)์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ณด๋‹ค ํ›จ์”ฌ ๋œ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ์ž‘์—…์„ ์™„๋ฃŒํ•  ๋งŒํผ ์ถฉ๋ถ„ํžˆ ๊ฐ•๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋” ์‚ฌ์šฉ์ž ์นœํ™”์ ์ผ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ ํ˜•์— ๋Œ€ํ•œ ์‚ฐ์ˆ  ์—ฐ์‚ฐ์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

ํŠนํžˆ ๋„๋ฉ”์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” TLD/ ๊ณต๊ฐœ ์ ‘๋ฏธ์‚ฌ ์œ ํšจ์„ฑ๋„ ํ™•์ธํ•˜๋Š” ๊ฒฝ์šฐ ์‹ค์ œ๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋งค์šฐ ๋ณต์žกํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค. RFC์— ๋”ฐ๋ฅธ ์ผ๋ฐ˜ ๋„๋ฉ”์ธ ์ž์ฒด๋Š” /[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)+/ + ์ตœ๋Œ€ 255์ž๋งŒํผ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ์œ„์˜ ์ •๊ทœ ํ‘œํ˜„์‹์—์„œ ๋ณด์—ฌ์ฃผ๋“ฏ์ด ์ „์ฒด ์ผ๋ฐ˜ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•œ ์ด๋งˆ์ €๋„ ์ž…๋ ฅํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. @rozzzly ๋˜๋Š” ๋‚ด ์ œ์•ˆ์˜ ๋ฌธ์ž์—ด๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ฒŒ ์œ ํ˜•์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ(๋…์ž์—๊ฒŒ ์—ฐ์Šต์šฉ์œผ๋กœ ๋‚จ๊ฒจ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค) ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ์—ฌ์ „ํžˆ ๋‹ค์†Œ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

@isiahmeadows

๊ทธ๊ฒƒ์€ ๋ช‡ ๊ฐ€์ง€ ์ž‘์€ ๊ธฐ๋Šฅ์ด ๋น ์ง„

์ด ์ „์ฒด ์Šค๋ ˆ๋“œ๋ฅผ ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฝ์€ ๊ฒƒ์€ 1๋…„์ด ํ›จ์”ฌ ๋„˜์—ˆ์Šต๋‹ˆ๋‹ค. ์‰ฌ๋Š” ์‹œ๊ฐ„์— ์•Œ๋ฆผ์„ ๋ณด๊ณ  @rugk๋‹˜ ์˜ _"๊ธ€์Ž„ ... "์ •๊ทœ์‹"์ด๋ผ๊ณ  ๋ถ€๋ฅด์ง€ ๋งˆ์„ธ์š” -

...์œ„์˜ regexp๊ฐ€ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์™„์ „ํ•œ ์ •๊ทœ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•œ ์ด๊ฒƒ์€ ์ž…๋ ฅํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. @rozzzly ๋˜๋Š” ๋‚ด ์ œ์•ˆ์˜ ๋ฌธ์ž์—ด๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋งค์šฐ ๊ฐ„๋‹จํ•˜๊ฒŒ ์œ ํ˜•์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ(๋…์ž์—๊ฒŒ ์—ฐ์Šต์šฉ์œผ๋กœ ๋‚จ๊ฒจ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค) ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ์—ฌ์ „ํžˆ ๋‹ค์†Œ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ์ƒ๊ฐ์—, ๋‚ด๊ฐ€ ์ œ์•ˆํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ์ œํ•œ๋œ ํŒจํ„ด ์ผ์น˜๋ฅผ ์œ„ํ•œ ์ผ๋ถ€ ๊ธฐ๋Šฅ์€ ๋งค์šฐ ๋‹จ์ˆœํ•˜๊ณ  _ํ•„์—ฐ์ ์œผ๋กœ ์—„๊ฒฉํ•˜์ง€ ์•Š์€_ ์—„๊ฒฉํ•œ ์ž…๋ ฅ์— ๋งค์šฐ ์œ ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ค€ ์˜ˆ๋Š” ์ •ํ™•ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ํญํŒŒ์‹œํ‚ค์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ @nikeee ์™€ ๋‹น์‹ ์ด ๋ชจ๋‘ ์ง€์ ํ–ˆ๋“ฏ์ด ์ด๊ฒƒ์€ ์œ„ํ—˜ํ•œ ๊ทน๋‹จ์œผ๋กœ ์ทจํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณต์šฉ์ฒด๋งŒ ์ง€์›ํ•˜๋Š” ๊ฐ€์žฅ ์ˆœ์ง„ํ•œ ๊ตฌํ˜„์„ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€ ๋‹ค์Œ์„ ํฌํ•จํ•˜๋Š” @types/some-popular-project ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฒŒ์‹œํ•˜์—ฌ ๋ชจ๋“  ์‚ฌ๋žŒ์˜ ํ•˜๋ฃจ๋ฅผ ๋ง์น  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

type MixedCaseAlphaNumeric = (
    | 'a'
    | 'b'
    | 'c'
    // and so on
);

type StrWithLengthBeteen1And64<Charset extends string> = (
    | `${Charset}`
    | `${Charset}|${Charset}`
    | `${Charset}|${Charset}|${Charset}`
    // and so on
);

function updatePassword(userID: number, password: StrWithLengthBetween1And64<MixedCaseAlphaNumeric>): void {
    // ...
}

๊ทธ๊ฒƒ์„ ๊ด€์ ์—์„œ ๋ณด๋ฉด, ๊ทธ ์—ฐํ•ฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์šฐ์ฃผ์˜ ์›์ž ๋ณด๋‹ค ๋” ๋งŽ์€ ๋ณ„๊ฐœ์˜ ์œ ํ˜•.

์ด์ œ, ๋‚˜๋Š” ๋”์ฐํ•˜๊ฒŒ ๊ธด ํ• ๋‹น ๊ฐ€๋Šฅ์„ฑ ์˜ค๋ฅ˜๋ฅผ ๋ณด์•˜์ง€๋งŒ ๊ทธ์— ๋Œ€ํ•œ (์ž˜๋ฆฌ์ง€ ์•Š์€) ์˜ค๋ฅ˜๋ฅผ ์ƒ์ƒํ•ด๋ณด์‹ญ์‹œ์˜ค.

Type '"๐Ÿ˜ข"' is not assignable to type '"a"|"b"|"c"..........."'.ts(2322)'

์˜ˆ.. ๊ฑฐ๊ธฐ์— ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@rozzzly ๊ทธ ์œ ํ˜•์ด TupleWithLengthBeteen1And64<Charset> ์™€ ๋‹ค๋ฅธ ์ (๊ฐ€๋Šฅ์„ฑ ์ธก๋ฉด์—์„œ)์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ชจ๋“  ์œ ํ˜•์„ ์ •๊ทœํ™”๋œ ํ˜•์‹์œผ๋กœ ํ™•์žฅํ•˜๋„๋ก ๊ฐ•์š”๋˜์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒ๋‹นํžˆ ์ •์ƒ์ ์ธ ์œ ํ˜•์—์„œ ๋น ๋ฅด๊ฒŒ ํญ๋ฐœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
"3๊ณผ 1024 ์‚ฌ์ด์˜ ์ •์ˆ˜"(๋ฉ”์‹œ์ง€ ๋ฒ„ํผ ํ• ๋‹น ๊ธธ์ด๋ฅผ ์ƒ๊ฐํ•จ)๋ผ๋„ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜๋Š” ๊ฒฝ์šฐ ์ด ๋ฌธ์ œ๊ฐ€ ํ˜„์žฌ typescript์—์„œ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@simonbuchan ์ตœ์†Œํ•œ ์ ‘๋‘์‚ฌ์™€ ์ ‘๋ฏธ์‚ฌ ์œ ํ˜•์ด ์กด์žฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋งŽ์€ DOM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋ ˆ์ž„์›Œํฌ์— ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ฃฝ๋„๋ก ๋‘๋“ค๊ฒจ ๋งž์•˜๊ณ  ๋ช‡ ๊ฐ€์ง€ ์ข‹์€ ์ œ์•ˆ์ด ์ด๋ฏธ ์ฃผ์–ด์กŒ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ผ๋ถ€ ์‚ฌ๋žŒ๋“ค์ด ์•ฝ๊ฐ„ ํฅ๋ฏธ๋กญ๊ฒŒ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š” ์ถ”๊ฐ€ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

์—ญ ์ฐธ์กฐ์— ์ง๋ฉดํ•˜์—ฌ ์—ฌ์ „ํžˆ ๊ฒฐ์ • ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋” ๋‚˜์˜์ง€๋Š” ์•Š๋”๋ผ๋„ ๊ธฐํ•˜ ๊ธ‰์ˆ˜์ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ๊ต์œก๋ฐ›์€ ์ถ”์ธก์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

์—ญ ์ฐธ์กฐ๋Š” regexp๊ฐ€ ์ปจํ…์ŠคํŠธ ํ”„๋ฆฌ ๋ฌธ๋ฒ•์˜ ์ƒ์œ„ ์ง‘ํ•ฉ์ธ ์ปจํ…์ŠคํŠธ์— ๋ฏผ๊ฐํ•œ ๋ฌธ๋ฒ•์„ โ€‹โ€‹์„ค๋ช…ํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  CFG์— ๋Œ€ํ•œ ์–ธ์–ด ํ‰๋“ฑ์€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์„ ํ˜• ์ œํ•œ ์ž๋™ ์žฅ์น˜์— ํ•ด๋‹นํ•˜๋Š” CSG์˜ ๊ฒฝ์šฐ ๋” ๋‚˜์ฉ๋‹ˆ๋‹ค.


DFA๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ •๊ทœ ํ‘œํ˜„์‹์ด ์ •๊ทœ ํ‘œํ˜„์‹(concat, union, star, ๊ต์ง‘ํ•ฉ, ๋ณด์ˆ˜ ๋“ฑ)์— ์‚ฌ์šฉ๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด ์ •๊ทœ ํ‘œํ˜„์‹์„ NFA๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์€ O(n)์ด๋ฉฐ 2์˜ ๊ณฑ์„ ์–ป์Šต๋‹ˆ๋‹ค. NFA๋Š” O(m*n)์ด๊ณ  ์ˆ˜๋ฝ ์ƒํƒœ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ ๊ทธ๋ž˜ํ”„๋ฅผ ์ˆœํšŒํ•˜๋Š” ๊ฒƒ์€ O(m*n)์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‘ ์ •๊ทœ ์ •๊ทœ์‹์˜ ์–ธ์–ด ๋™๋“ฑ์„ฑ/๋ถ€๋ถ„์ง‘ํ•ฉ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋„ O(m*n)์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” ์—ฌ๊ธฐ์„œ ์•ŒํŒŒ๋ฒณ์ด ์ •๋ง ํฌ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ต๊ณผ์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ DFA/NFA/์ •๊ทœ ํ‘œํ˜„์‹์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•  ๋•Œ ํฌ๊ธฐ๊ฐ€ 1-5์ธ ์•ŒํŒŒ๋ฒณ์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ JS ์ •๊ทœ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  ์œ ๋‹ˆ์ฝ”๋“œ๊ฐ€ ์•ŒํŒŒ๋ฒณ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก , ํ‰๋“ฑ/๋ถ€๋ถ„์ง‘ํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํฌ์†Œ ๋ฐฐ์—ด ๋ฐ ๊ธฐํƒ€ ์˜๋ฆฌํ•œ ํ•ดํ‚น ๋ฐ ์ตœ์ ํ™”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „ํ™˜ ํ•จ์ˆ˜๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค...

์ •๊ทœ ๋Œ€ ์ •๊ทœ ํ• ๋‹น์— ๋Œ€ํ•œ ์œ ํ˜• ๊ฒ€์‚ฌ๋ฅผ ๋‹ค์†Œ ํšจ์œจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ชจ๋“  ๋น„์ •๊ทœ ํ• ๋‹น์—๋Š” ๋ช…์‹œ์  ํ˜•์‹ ์–ด์„ค์…˜๋งŒ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ตœ๊ทผ์— ์ž‘์€ ์œ ํ•œ ์ž๋™ ์žฅ์น˜ ํ”„๋กœ์ ํŠธ์—์„œ ์ผํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ •๋ณด๊ฐ€ ์—ฌ์ „ํžˆ ๋‚ด ๋งˆ์Œ์— ์‹ ์„ ํ•ฉ๋‹ˆ๋‹ค =x

๋‚ด๊ฐ€ ์ฐฉ๊ฐํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋ฉด ์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ์—„์ฒญ๋‚œ ์œ ํ˜•์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๋ คํ•˜๋‹ค:

type TLD = 'com' | 'net' | 'org' | 'ly' | 'a' | 'b' | 'c' | 'd';
type Foo = `${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}${TLD}`;
type Bar = `${Foo}${Foo}${Foo}${Foo}${Foo}`

(์ด๊ฒƒ์€ ๊ตฌํ˜„์—์„œ ๊ณต์šฉ์ฒด ์œ ํ˜•์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ/๋” ๋ณต์žกํ•œ ๊ตฌํ˜„์—์„œ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Œ)

์žฌ๋ฏธ์žˆ๊ฒŒ๋„ ์ด๊ฒƒ์ด ๋ฐ”๋กœ ์ƒˆ๋กœ์šด ํ…œํ”Œ๋ฆฟ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์œผ๋กœ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ๋Š” ๊ณต์šฉ์ฒด ์œ ํ˜•์— ๋Œ€ํ•œ ์ž„๊ณ„๊ฐ’์„ ๊ฐ€์ง์œผ๋กœ์จ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@AnyhowStep JS ์—ญ์ฐธ์กฐ๋Š” ์œ ์ผํ•˜๊ฒŒ ์ƒํ™ฉ์— ๋งž๋Š” ํ”„๋กœ๋•์…˜(๊ทธ๋ฆฌ๊ณ  ์ƒ๋‹นํžˆ ๊ฐ„๋‹จํ•˜๊ณ  ์ œํ•œ๋œ ๊ฒƒ - ์ตœ๋Œ€ 9๊ฐœ์˜ ๊ทธ๋ฃน๋งŒ ์ด์™€ ๊ฐ™์ด ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Œ)์ด๋ฉฐ ๋‚˜๋จธ์ง€ ์ •๊ทœ ํ‘œํ˜„์‹ ๋ฌธ๋ฒ•์€ ๊ทœ์น™์ ์ด๋ฏ€๋กœ ์˜์‹ฌ๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์ด ์–ด๋–ค ์˜๋ฏธ์—์„œ๋“  ์‹ค์šฉ์ ์ด์ง€ ์•Š๋‹ค๋Š” ๋ฐ ๋™์˜ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ™‚

ํŽธ์ง‘: ์ •ํ™•๋„

@rozzzly์˜ ์ด ๋Œ“๊ธ€ ์€ ์•ผ๊ฐ„์— TS 4.1.0์—์„œ ์ž‘๋™ํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค!

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type Url = `${'http'|'https'}://${Domain}`;

const success: Url = 'https://example.com';
const fail: Url = 'example.com';
const domain: Domain = 'example.com';

ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ์—์„œ ์‹œ๋„ํ•ด ๋ณด๊ณ  fail ์— ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š” ๐Ÿคฉ


์—…๋ฐ์ดํŠธ : ์ด ๊ธฐ๋Šฅ์„ ์กฐ๊ธˆ ์‚ฌ์šฉํ•ด ๋ณธ ํ›„์—๋Š” ๋งŽ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, 16์ง„์ˆ˜ ์ƒ‰์ƒ ๋ฌธ์ž์—ด์—๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type HexColor = `#${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}`;
let color: HexColor = '#123456';

์˜ค๋Š˜์€ "ํ‘œํ˜„ํ•˜๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋ณต์žกํ•œ ๊ณต์šฉ์ฒด ์œ ํ˜•์„ ์ƒ์„ฑํ•˜๋Š” ํ‘œํ˜„์‹(2590)"์œผ๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค

@rozzzly์˜ ์ด ๋Œ“๊ธ€ ์€ ์•ผ๊ฐ„์— TS 4.1.0์—์„œ ์ž‘๋™ํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค!

type TLD = 'com' | 'net' | 'org';
type Domain = `${string}.${TLD}`;
type Url = `${'http'|'https'}://${Domain}`;

const success: Url = 'https://example.com';
const fail: Url = 'example.com';
const domain: Domain = 'example.com';

ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ์—์„œ ์‹œ๋„ํ•ด ๋ณด๊ณ  fail ์— ์ปดํŒŒ์ผ ์‹œ๊ฐ„ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š” ๐Ÿคฉ

์ด๊ฒƒ์€ ์ธ๋ฑ์Šค์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์šฐ๋ฆฌ ๋Œ€๋ถ€๋ถ„์ด UX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ง๋ฉดํ•˜๋Š” ๋ฐ์ดํ„ฐ ๋˜๋Š” ์•„๋ฆฌ์•„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๊ฒƒ์€ TS๊ฐ€ ๋ฌธ์ž์—ด๋งŒ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. | ์ˆซ์ž. ์ด๊ฒƒ์€ ๋ณธ์งˆ์ ์œผ๋กœ ๋ฌธ์ž์—ด์ด๋ฏ€๋กœ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
https://www.typescriptlang.org/play?target=99&ts=4.1.0-dev.20201001#code/LAKALgngDgpgBAEQIZicgzgCzgXjgAwBIBvAcgBMUkBaUgXxPTACcBLAOwHM78BuUDmBjMAZkgDG8AJIAVGEzjFQcFXADalVBkwAFZgHsoALmRakWALpGmbLvxB1QocfvYL0AV3GT06I3Fl5MFxFCipqISZSI1JIsHpeIA

_์—…๋ฐ์ดํŠธ_: ์ด ๊ธฐ๋Šฅ์„ ์กฐ๊ธˆ ์‚ฌ์šฉํ•ด ๋ณธ ํ›„์—๋Š” ๋งŽ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, 16์ง„์ˆ˜ ์ƒ‰์ƒ ๋ฌธ์ž์—ด์—๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

type HexChar = '0' | '1' | '2' | '3' | '4' | '5' | '6'| '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F';
type HexColor = `#${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}${HexChar}`;
let color: HexColor = '#123456';

์˜ค๋Š˜์€ "ํ‘œํ˜„ํ•˜๊ธฐ์—๋Š” ๋„ˆ๋ฌด ๋ณต์žกํ•œ ๊ณต์šฉ์ฒด ์œ ํ˜•์„ ์ƒ์„ฑํ•˜๋Š” ํ‘œํ˜„์‹(2590)"์œผ๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค

๋ฆด๋ฆฌ์Šค ์ •๋ณด์— ์ด ์ œํ•œ ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์œ ํšจํ•œ ์กฐํ•ฉ์˜ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ 16,777,216(์ฆ‰, 16^6) ๋ฉค๋ฒ„๊ฐ€ ์žˆ๋Š” ๊ณต์šฉ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ข‹์€ ์•„์ด๋””์–ด์ž…๋‹ˆ๋‹ค... Igmat๋Š” 2016๋…„์— ์ข…์ด๋กœ ๋ณด๊ธฐ์—๋„ ๊ดœ์ฐฎ์•„ ๋ณด์ด๋Š” ๋ช‡ ๊ฐ€์ง€ ๋†€๋ผ์šด ๊ฒŒ์‹œ๋ฌผ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ๊ฐœ์ฒด ๋ฆฌํ„ฐ๋Ÿด์˜ ํ‚ค๊ฐ€ ์œ ํšจํ•œ CSS ํด๋ž˜์Šค ์ด๋ฆ„์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค... ํ•˜์ง€๋งŒ ์ œ๊ฐ€ ๋ณด๊ธฐ์—๋Š” typescript๊ฐ€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์ด ๋„ˆ๋ฌด๋„ ๋ถ„๋ช…ํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. ํŠนํžˆ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์„ ํ•˜๋“œ ์ฝ”๋”ฉํ•˜๊ณ  typescript๊ฐ€ ๋‹ค์Œ์„ ์•Œ์•„๋‚ผ ํ•„์š”๊ฐ€ ์—†๋Š” ์ƒํ™ฉ์—์„œ๋Š” ๋”์šฑ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. MyUnionExtended์ด๊ตญ์ ์œ ํ˜•์€ SomeArbitraryRegexType์„ ์ถฉ์กฑํ•ฉ๋‹ˆ๋‹ค.

์–ธ์  ๊ฐ€๋Š” ๋” ์ƒ์‚ฐ์ ์ธ ๊ธฐ์—ฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ ์ง€์‹์„ ๊ฐ–์ถ”๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค./

@rozzzly์˜ ์ด ๋Œ“๊ธ€ ์€ ์•ผ๊ฐ„์— TS 4.1.0์—์„œ ์ž‘๋™ํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค!

์šฐ์™€. ๋‚˜๋Š” ์†”์งํžˆ ์ด๊ฒƒ์ด ๊ตฌํ˜„๋˜๋Š” ๊ฒƒ์„ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ ์–ด๋„ ์กฐ๋งŒ๊ฐ„์€ ์•„๋‹™๋‹ˆ๋‹ค.

@chadlavi-casebook

๋ฆด๋ฆฌ์Šค ์ •๋ณด์— ์ด ์ œํ•œ ์‚ฌํ•ญ์— ๋Œ€ํ•œ ์–ธ๊ธ‰์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ์œ ํšจํ•œ ์กฐํ•ฉ์˜ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ 16,777,216(์ฆ‰, 16^6) ๋ฉค๋ฒ„๊ฐ€ ์žˆ๋Š” ๊ณต์šฉ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋˜๊ธฐ ์ „์— ํ•ด๋‹น ์กฐํ•ฉ์ด ์–ผ๋งˆ๋‚˜ ์ปค์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. @styfle ์˜ ์˜ˆ๋Š” ๊ทธ

@thehappycheese

๋‚ด ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์˜ ํ‚ค๊ฐ€ ์œ ํšจํ•œ CSS ํด๋ž˜์Šค ์ด๋ฆ„์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ตฌํ˜„์œผ๋กœ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์ž์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜๋Ÿ‰์ž์™€ ๋ฒ”์œ„์— ๋Œ€ํ•œ ์ง€์›์ด ์žˆ๋Š” ๊ฒฝ์šฐ BEM ์Šคํƒ€์ผ ํด๋ž˜์Šค ์ด๋ฆ„์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ํ‘œ์ค€ js ์ •๊ทœ์‹์€ _๋„ˆ๋ฌด_ ๋”์ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
^\.[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$
๋‹น์‹ ์€ ๋˜ํ•œ ์•ต์ปค๋ฅผ ๋ฒ„๋ฆด ๊ฒƒ์ธ๋ฐ, ๊ทธ ์ด์œ ๋Š” ๊ตฌํ˜„์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”์™€ ๊ฐ™์ด ์ข…๋‹จ ๊ฐ„ ์ผ์น˜์ด๊ฑฐ๋‚˜ ์•„๋ฌด๊ฒƒ๋„ ์•„๋‹ˆ๋ฏ€๋กœ ^ ๋ฐ $ ๊ฐ€ ์•”์‹œ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด์ œ ์œ ํšจํ•œ CSS ์„ ํƒ๊ธฐ์˜ ์ข์€ ํ•˜์œ„ ์ง‘ํ•ฉ์— ๋Œ€ํ•œ ๋น„๊ต์  ๊ฐ„๋‹จํ•œ ์ •๊ทœ์‹์ž…๋‹ˆ๋‹ค. ์˜ˆ: เฒ _เฒ  ๋Š” ์œ ํšจํ•œ ํด๋ž˜์Šค ์ด๋ฆ„์ž…๋‹ˆ๋‹ค. ๋†๋‹ด์ด ์•„๋ƒ.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์„ํ•ด์•ผํ–ˆ๋‹ค.

TypeScript์—์„œ ์ผ๋ฐ˜ ์–ธ์–ด๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋†€์ดํ„ฐ #1 , 1์˜ ์ˆ˜๋Š” 3์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์ง€๋งŒ 2๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋” ์ •ํ™•ํ•˜๊ฒŒ๋Š” TS 4.1์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ๊ฒฐ์ •๋ก ์  ์œ ํ•œ ์˜คํ† ๋งˆํ†ค์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ๋ง์€, ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ TS์—์„œ ํŠœ๋ง ๊ธฐ๊ณ„๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ DFA์™€ PDA๋Š” ๊ทธ์— ๋น„ํ•ด "์‰ฝ์Šต๋‹ˆ๋‹ค".

๊ทธ๋ฆฌ๊ณ  ํ…œํ”Œ๋ฆฟ ๋ฌธ์ž์—ด์€ ์ด๊ฒƒ์„ ๋” ์œ ์šฉํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ์œ ํ˜•์€ ์‹ค์ œ๋กœ ๊ฐ„๋‹จํ•˜๊ณ  < 30 LOC์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

type Head<StrT extends string> = StrT extends `${infer HeadT}${string}` ? HeadT : never;

type Tail<StrT extends string> = StrT extends `${string}${infer TailT}` ? TailT : never;

interface Dfa {
    startState : string,
    acceptStates : string,
    transitions : Record<string, Record<string, string>>,
}

type AcceptsImpl<
    DfaT extends Dfa,
    StateT extends string,
    InputT extends string
> =
    InputT extends "" ?
    (StateT extends DfaT["acceptStates"] ? true : false) :
    AcceptsImpl<
        DfaT,
        DfaT["transitions"][StateT][Head<InputT>],
        Tail<InputT>
    >;

type Accepts<DfaT extends Dfa, InputT extends string> = AcceptsImpl<DfaT, DfaT["startState"], InputT>;

์–ด๋ ค์šด ๋ถ€๋ถ„์ธ ์ž๋™ ์žฅ์น˜๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ˆ„๊ตฐ๊ฐ€ TypeScript DFAโ„ข ์ƒ์„ฑ๊ธฐ์— ๋Œ€ํ•œ ์ •๊ทœ์‹์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค...


๋˜ํ•œ "๊ธธ์ด 6์˜ 16์ง„ ๋ฌธ์ž์—ด" ์˜ˆ์ œ์—์„œ ๋ชป์ƒ๊ธด ํ•ด์ปค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๊ทœ์‹๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ฌธ์ž์—ด๋งŒ ํ—ˆ์šฉํ•˜๋„๋ก ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ์„ ๊ฐ•์กฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

declare function takesOnlyHex<StrT extends string> (
    hexString : Accepts<HexStringLen6, StrT> extends true ? StrT : {__err : `${StrT} is not a hex-string of length 6`}
) : void;

//OK
takesOnlyHex("DEADBE")

//Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "DEADBEEF is not a hex-string of length 6"; }'.
takesOnlyHex("DEADBEEF")

//OK
takesOnlyHex("01A34B")

//Error: Argument of type 'string' is not assignable to parameter of type '{ __err: "01AZ4B is not a hex-string of length 6"; }'.
takesOnlyHex("01AZ4B")

์—ฌ๊ธฐ ๋ณด๋„ˆ์Šค ๋†€์ดํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค . ์ •๊ทœ์‹ /^hello .*/ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋˜ ๋‹ค๋ฅธ ๋†€์ดํ„ฐ ; ์ •๊ทœ์‹ / world$/ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ์˜ˆ์ธ Playground ; ์ด๊ฒƒ์€ ๋ถ€๋™ ์†Œ์ˆ˜์  ๋ฌธ์ž์—ด ์ •๊ทœ์‹์ž…๋‹ˆ๋‹ค !

@AnyhowStep ๊ธ€์Ž„, ๋‚˜๋Š” ๋‹น์‹ ์˜ DFA ์•„์ด๋””์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ์ •๊ทœ์‹ [abc]{4} ๋ฅผ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ฌธ์ž abc๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ์ง€๋งŒ ์ •ํ™•ํžˆ ๊ธธ์ด๊ฐ€ 4์ž„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. (aaaa, abcc, bbcc ๋“ฑ...).
์šด๋™์žฅ

https://cyberzhg.github.io/toolbox/min_dfa?regex=ZCgoYmQqYiopKmMpKg==

https://github.com/CyberZHG/toolbox

์˜์ง€๋ ฅ์ด ๋” ์žˆ์—ˆ๋‹ค๋ฉด ์œ„์™€ ๊ฐ™์€ ๊ฒƒ์„ ๊ฐ€์ ธ์™€ ์ •๊ทœ์‹์„ TS DFAsโ„ข๋กœ ๋ฐ”๊พธ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. lol

์ข‹์•„์š”, ๋ฐฉ๊ธˆ ํ”„๋กœํ† ํƒ€์ž…์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

https://glitch.com/~sassy-valiant-heath

[ํŽธ์ง‘] https://glitch.com/~efficacious-valley-repair <-- ์ด๊ฒƒ์€ ๋” ๋ณต์žกํ•œ ์ •๊ทœ์‹์— ๋Œ€ํ•ด ๋” ๋‚˜์€ ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

[ํŽธ์ง‘] Glitch๋Š” ๋„ˆ๋ฌด ์˜ค๋žซ๋™์•ˆ ๋น„ํ™œ์„ฑ ์ƒํƒœ์ธ ๋ฌด๋ฃŒ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณด๊ด€ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ํŒŒ์ผ์ด ์žˆ๋Š” git repo๊ฐ€ โ€‹โ€‹์žˆ์Šต๋‹ˆ๋‹ค.
https://github.com/AnyhowStep/efficacious-valley-repair/tree/main/app

1๋‹จ๊ณ„, ์—ฌ๊ธฐ์— ์ •๊ทœ์‹์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
image

2๋‹จ๊ณ„, ๋ณ€ํ™˜์„ ํด๋ฆญํ•˜๊ณ ,
image

3๋‹จ๊ณ„, ์ƒ์„ฑ๋œ TS ํ”Œ๋ ˆ์ด๊ทธ๋ผ์šด๋“œ URL์„ ํด๋ฆญํ•˜๊ณ ,
image

4๋‹จ๊ณ„, InLanguage_0 ๊นŒ์ง€ ์•„๋ž˜๋กœ ์Šคํฌ๋กคํ•ฉ๋‹ˆ๋‹ค.
image

5๋‹จ๊ณ„, ์ž…๋ ฅ ๊ฐ’์œผ๋กœ ์žฌ์ƒ,
image

image

๋ณ€ํ™˜์˜ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ https://www.npmjs.com/package/regex2dfa์˜ ์ž‘์„ฑ์ž์ธ @kpdyer์—๊ฒŒ ๊ฐ์‚ฌ ๋ฅผ ์ „ํ•ฉ๋‹ˆ๋‹ค.

์กฐ๊ธˆ ๋” ๊ฐ•๋ ฅํ•œ ๊ฒƒ์ด ํ•„์š”ํ•œ ์‚ฌ๋žŒ์„ ์œ„ํ•ด ํŠœ๋ง ๋จธ์‹ ์ด ์žˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜†

์šด๋™์žฅ

์ด ์Šค๋ ˆ๋“œ๋Š” ๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ฝ์„ ์ˆ˜ ์—†์œผ๋ฉฐ ๋งŽ์€ ์ฃผ์„์ด ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด ์œ ํ˜•์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ฑฐ๋‚˜ ์ฃผ์ œ์—์„œ ๋ฒ—์–ด๋‚ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์œผ๋กœ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋‚˜๋จธ์ง€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ๋ฌธ์ œ #41160์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์œ ํ˜• ์‹œ์Šคํ…œ ํŒŒ์„œ์— ๋Œ€ํ•ด ๊ณ„์† ๋…ผ์˜ํ•˜์‹ญ์‹œ์˜ค ๐Ÿ˜€

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰