[TS] ํ์ ์คํฌ๋ฆฝํธ ๊ตฌ์กฐ์ ํ์ดํ ํ์ฉํ๊ธฐ
Object.keys ๋ฉ์๋์ ํ์ ์ ์
ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ์๋ ๊ฐ์ ์ํฉ์ ์์ฃผ ๋ง์ฃผํ๋ค. Object.keys()
๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ๊ฐ์ฒด์ ํค๋ฅผ ๋ฐฐ์ด๋ก ์ถ์ถํ ํ, ํด๋น ํค๋ฅผ ์ด์ฉํด ๊ฐ์ฒด์ ์ ๊ทผํ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
type Options = { host: string; port: number };
const validateOptions = (options: Options) => {
const keys = Object.keys(options); // string[]
keys.forEach((key) => {
// Error! 'Options' ํ์์์ 'string' ํ์์ ๋งค๊ฐ ๋ณ์๊ฐ ํฌํจ๋ ์ธ๋ฑ์ค ์๊ทธ๋์ฒ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
if (options[key] === null) {
throw new Error(`Missing option ${key}`);
}
});
};
์ ๋ฌธ์ ๋ ์๋์ฒ๋ผ as
ํค์๋๋ฅผ ์ฌ์ฉํด์ ํ์
๋จ์ธํ๋ฉด ์ฝ๊ฒ ํด๊ฒฐํ ์ ์๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ ์ ๋ฐ์ํ๋๊ฑธ๊น?
const validateOptions = (options: Options) => {
const keys = Object.keys(options) as (keyof Options)[];
// ...
};
Object.keys
ํ์
์ ์๋ฅผ ์ดํด๋ณด๋ฉด ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์ ํญ์ string[]
์ ๋ฐํํ๋๋ก ๋์ด ์๋ค.
// lib.es5.d.ts
interface ObjectConstructor {
// ...
keys(o: object): string[];
}
Object.keys()
๋ฉ์๋๊ฐ ๊ฐ์ฒด ์ ๋ค๋ฆญ ํ์
T
๋ฅผ ๋ฐ์ (keyof T)[]
๋ฅผ ๋ฐํํ๋ค๋ฉด ์ด๋ฌํ ๋ฌธ์ ๋ ๋ฐ์ํ์ง ์๋๋ค. ํ์
์คํฌ๋ฆฝํธ๊ฐ ์ด๋ ๊ฒ ํ์
์ ์ ์ํ์ง ์์ ์ด์ ๋ ๋ฌด์์ผ๊น? ์ด๋ ๊ตฌ์กฐ์ ํ์
์์คํ
๊ณผ ๊ด๋ จ ์๋ค.
export {}; // declare global์ ์ฌ์ฉํ๊ธฐ ์ํด ์ธ๋ถ ๋ชจ๋๋ก ์ธ์ํ๋๋ก ๋ง๋ฆ
declare global {
interface ObjectConstructor {
// ObjectConstructor ์ธํฐํ์ด์ค๋ฅผ ํ์ฅํ์ฌ Object.keys ๋ฉ์๋ ํ์
๋ฎ์ด์ฐ๊ธฐ
keys<T extends object>(o: T): (keyof T)[];
}
}
const validateOptions = (options: Options) => {
const keys = Object.keys(options); // (keyof Options)[]
// ...
};
๐ก ํ์
์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ๋ export
ํน์ import
๊ตฌ๋ฌธ์ด ์๋ ํ์ผ์ ์ผ๋ฐ ์คํฌ๋ฆฝํธ๋ก ์ทจ๊ธํ๋ค. declare global
์ ์ด์ฉํด ์ ์ญ ์ฐธ์กฐํ ์ ์๋ ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ๋ ค๋ฉด export {}
๋ฑ์ ์ฌ์ฉํด ์ธ๋ถ ๋ชจ๋๋ก ์ธ์ํ๋๋ก ํด์ผ ํ๋ค.
๊ตฌ์กฐ์ ํ์ดํ Structural Typing
๊ตฌ์กฐ์ ํ์ดํ(Structural Typing)์ ์ฝ๋ ๊ตฌ์กฐ์ ๊ด์ ์์ ํ์ ํธํ์ฑ์ ํ๋จํ๋ ๋ฐฉ์์ ์๋ฏธํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ฒด ์์ฑ ์, ํจ์ ํ๋ผ๋ฏธํฐ ์ ๋ฑ์ด ๋น๊ต ๋์๋ณด๋ค ๋ง์ ๊ฒฝ์ฐ, ๊ตฌ์กฐ์ ์ผ๋ก ๋ ํฐ ํ์ ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
์๋ ์์์์ User
ํ์
์ name
, age
์์ฑ์ ๊ฐ๋๋ค. ํ์ง๋ง city
๋ผ๋ ์ถ๊ฐ ์์ฑ์ ๊ฐ๋ userThree
๊ฐ์ฒด๋ฅผ ํจ์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ฒจ๋ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.
type User = { name: string; age: number };
const saveUser = (user: User) => {};
const userOne = { name: 'One', age: 25 };
saveUser(userOne); // Ok
const userTwo = { name: 'Two' };
saveUser(userTwo); // ts(2345) Error! 'age' ์์ฑ์ด '{ name: string; }' ํ์์ ์์ง๋ง 'User' ํ์์์ ํ์์
๋๋ค
const userThree = { name: 'Three', age: 35, city: 'Seoul' };
saveUser(userThree); // OK
๊ตฌ์กฐ์ ํ์
์์คํ
์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์
A
๊ฐ B
์ ์ํผ์
์ธ ๊ฒฝ์ฐ(A
๋ B
์ ๋ชจ๋ ์์ฑ์ ํฌํจํ๊ณ ์ถ๊ฐ ์์ฑ๋ ๊ฐ์ง ๋) A
๋ฅผ B
์ ํ ๋นํ ์ ์๋ค. (๊ณต๋ณ์ฑ, ๋ฐ๊ณต๋ณ์ฑ๊ณผ๋ ๋ณ๋์ ๊ฐ๋
)
- ์ํผ์
A
๋ ์๋ธ์ B
์ ํ ๋นํ ์ ์๋ค - ์๋ธ์
B
๋ ์ํผ์ A
์ ํ ๋นํ ์ ์๋ค
type Super = { name: string; age: number };
type Sub = { name: string };
const aSuper: Super = { name: 'super', age: 25 };
const aSub: Sub = { name: 'sub' };
const a: Sub = aSuper; // OK
const b: Super = aSub; // ts(2741) Error! 'age' ์์ฑ์ด 'Sub' ํ์์ ์์ง๋ง 'Super' ํ์์์ ํ์์
๋๋ค
์ ๊ฐ์ ํน์ฑ ๋๋ฌธ์ ํ์
์คํฌ๋ฆฝํธ์์ ํน์ ๊ฐ์ฒด ํ์
T
๋ฅผ ๋ค๋ฃฐ ๋ ๊ตฌ์กฐ์ ํ์ดํ ํน์ฑ์ ๋ฐ๋ผ ํด๋น ๊ฐ์ฒด๊ฐ ํ์
T
์ ํ๋กํผํฐ๋ฅผ ์ ์ด๋ ํ๋ ์ด์ ํฌํจํ๊ณ ์๋์ง๋ง ํ์ธํ ์ ์๋ค.
์ฆ, ํด๋น ๊ฐ์ฒด๊ฐ ์ ํํ ํ์
T์ ํ๋กํผํฐ๋ง ๊ฐ๋์ง, ์๋๋ฉด ์ถ๊ฐ์ ์ธ ํ๋กํผํฐ๋ ๊ฐ๋์ง ์ ์ ์๋ค๋ ๋ง์ด๋ค. ๋๋ฌธ์ Object.keys
๋ฉ์๋๋ ๊ฐ ํ๋กํผํฐ์ ์ ํํ ํ์
์ ๋ณด๊ฐ ์๋, ๊ฐ์ฒด ํ๋กํผํฐ ํค๋ฅผ ๋ชจ๋ string
์ผ๋ก ์ฒ๋ฆฌํด์ ๋ฐํํ๋ ๊ฒ์ด๋ค.
Object.keys ์ฌ์ฉ์ ์ฃผ์ํ ์
๐ก a ||= b
๋ฌธ๋ฒ์ ES2021์ ๋์
๋ ์ ๊ท ๊ธฐ๋ฅ์ผ๋ก a
๊ฐ falsy์ด๋ฉด b
๋ฅผ a
์ ํ ๋นํ๋ค — ์ฐธ๊ณ ํฌ์คํ
์๋ ์์์์ validateUser
ํจ์์ ํ๋ผ๋ฏธํฐ๋ User
๊ฐ์ฒด ํ์
์ ๋ฐ๋๋ก ์์ฑํ์ง๋ง, ๊ตฌ์กฐ์ ํ์ดํ ํน์ฑ์ผ๋ก ์ธํด email
์ถ๊ฐ ์์ฑ์ ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธธ ์ ์๋ค. ์ฝ๋๋ฅผ ์คํํ๋ฉด(๋ฐํ์) validators
๊ฐ์ฒด์ email
๋ฉ์๋๊ฐ ์์ผ๋ฏ๋ก “validate is not a function” ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
๋คํํ ์ฝ๋๋ฅผ ์คํํ์ง ์๋๋ผ๋(์ปดํ์ผ ์์ ) Object.keys()
๋ฉ์๋๋ ํญ์ string[]
ํ์
์ ๋ฐํํ๋ฏ๋ก validators[key]
๋ถ๋ถ์์ ํ์
์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ํ์
์คํฌ๋ฆฝํธ๋ key
๊ฐ string
ํ์
์ธ ๊ฒ์ ์๊ณ ์์ง๋ง ๊ฐ ๋ฌธ์์ด์ด validators
๊ฐ์ฒด์ ํค์ธ์ง๋ ์ ์ ์๊ธฐ ๋๋ฌธ์ ํ์
์์ ์ฑ์ ๋ณด์ฅํ ์ ์์ด์ ๊ทธ๋ฐ ๊ฒ.
๋ง์ฝ Object.keys()
๋ฉ์๋๊ฐ string[]
์ด ์๋ validators
๊ฐ์ฒด์ ํค ์ด๋ฆ์ ์์๋ก ๊ฐ๋ ๋ฐฐ์ด ํ์
์ ๋ฐํํ๋ค๋ฉด, ์๋์ฒ๋ผ ์ถ๊ฐ์ ์ธ ์์ฑ์ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋๊ฒผ์ ๋ ์ปดํ์ผ ์์ ์๋ ์๋ฌ๋ฅผ ์บ์นํ์ง ๋ชปํ๊ณ ๋ฐํ์ ์์ ์ ์๋ฌ๊ฐ ๋ฐ์ํ์ ๊ฒ์ด๋ค.
type User = { name: string; password: string };
const validators = {
name: (name: string) => { ... },
password: (password: string) => { ... },
};
const validateUser = (user: User) => {
let error = '';
const keys = Object.keys(user); // string[]
for (const key of keys) {
const validate = validators[key]; // ์ปดํ์ผ Error! Expression of type 'string' can't be used to index type { ... }
error ||= validate(user[key]); // ๋ฐํ์ Error! validate is not a function
}
return error;
};
const user = { name: 'John', password: '1q2w3e4r', email: 'john@gmail.com' };
validateUser(user); // email ์ถ๊ฐ ํ๋กํผํฐ๋ก ์ธํ ์๋ฌ๋ ๋ฐ์ํ์ง ์์
์ ์์์ ํ์
์๋ฌ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด ํ์
๊ฐ๋๋ฅผ ์ฌ์ฉํด key
๊ฐ validators
๊ฐ์ฒด์ ํค ์ค ํ๋์์ ์๋ ค์ค์ผ ํ๋ค. ํ์
๊ฐ๋๋ ํน์ ์ค์ฝํ์ ์๋ ํ์
์ ๋ฐํ์ ๋ ์ฒดํฌํ๋ ๋ฐฉ๋ฒ์ผ๋ก is
, typeof
, instanceof
๋ฑ์ ํค์๋๋ฅผ ํ์ฉํ๋ค. ์๋๋ is
ํค์๋๋ฅผ ์ด์ฉํด ํ์
์ ๊ฒ์ฌํ๋ ์์.
// ...
const isKeyOfValidators = (key: string): key is keyof typeof validators => {
return key in validators;
};
const validateUser = (user: User) => {
let error = '';
const keys = Object.keys(user); // string[]
for (const key of keys) {
if (isKeyOfValidators(key)) {
const validate = validators[key];
error ||= validate(user[key]);
}
}
return error;
};
๊ตฌ์กฐ์ ํ์ดํ ํ์ฉํ๊ธฐ โญ
๊ตฌ์กฐ์ ํ์ดํ์ ๋ง์ ์ ์ฐํจ์ ์ ๊ณตํ๋ค. ์๋ getKeyboardShortcut
ํจ์๋ KeyboardEvent
๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ๋จ์ถํค ์กฐํฉ์ ํ์ธํ๊ณ ๋์ํ๋ ๋ฌธ์์ด์ ๋ฐํํ๋ค.
function getKeyboardShortcut(e: KeyboardEvent) {
// ์ฐธ๊ณ ๋ก ๋ฉํ ํค๋ ์๋์ฐ์์ windows ํค, macOS์์ command ํค
if (e.key === 's' && e.metaKey) return 'save';
if (e.key === 'o' && e.metaKey) return 'open';
return null;
}
์ ํจ์๊ฐ ์์๋๋ก ์๋ํ๋์ง ํ์ธํ๊ธฐ ์ํด ์๋์ฒ๋ผ ๋ช ๊ฐ์ง ๋จ์ ํ
์คํธ๋ฅผ ์์ฑํ ์ ์๋ค. ํ์ง๋ง ํจ์ ์ธ์๋ก ๋๊ธด ๊ฐ์ฒด๋ KeyboardEvent
ํ์
์ ์๋ altKey
, code
๋ฑ 37๊ฐ ์์ฑ์ด ์๋ค๋ ํ์
์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
expect(getKeyboardShortcut({ key: 's', metaKey: true })).toEqual('save');
expect(getKeyboardShortcut({ key: 'o', metaKey: true })).toEqual('open');
expect(getKeyboardShortcut({ key: 's', metaKey: false })).toEqual(null);
์์ ๋ฐฉํธ์ผ๋ก as
ํค์๋๋ฅผ ์ฌ์ฉํด ํ์
๋จ์ธํ ์ ์์ง๋ง ์ ์ฌ์ ์ผ๋ก ๋ฐ์ํ ์ ์๋ ๋ค๋ฅธ ํ์
์๋ฌ๋ฅผ ๋์น ์ ์๋ ๋จ์ ์ด ์๋ค.
getKeyboardShortcut({ key: 's', metaKey: true } as KeyboardEvent);
์์์ ์ดํด๋ณธ ๊ตฌ์กฐ์ ํ์ดํ ํน์ฑ์ ํ์ฉํ๋ฉด ์ด ๋ฌธ์ ๋ฅผ ๋์ฑ ๊น๋ํ๊ฒ ํด๊ฒฐํ ์ ์๋ค. ์๋์ฒ๋ผ ํจ์ ํ๋ผ๋ฏธํฐ์ ํ์ํ ํค๋ณด๋ ์ด๋ฒคํธ ์์ฑ๋ง KeyboardShortcutEvent
ํ์
์ผ๋ก ์ ์ํด๋๋ฉด ํ์
์๋ฌ๋ฅผ ํผํ ์ ์๋ค.
interface KeyboardShortcutEvent {
key: string;
metaKey: boolean;
}
function getKeyboardShortcut(e: KeyboardShortcutEvent) {
// ...
}
์์ฒ๋ผ ์์ฑํ๋ฉด getKeyboardShortcut
ํจ์๊ฐ KeyboardEvent
ํ์
์ ๋ ์ข
์์ ์ด๊ฒ ๋์ด ๋ ๋ค์ํ ์ปจํ
์คํธ์์ ์ฌ์ฉํ ์ ์๋ ์ฅ์ ๋ ์๋ค. ์๋ ์ฝ๋์ฒ๋ผ ๋ชจ๋ KeyboardEvent
ํ๋กํผํฐ๋ฅผ ๊ฐ์ง๋ ์ด๋ฒคํธ ๊ฐ์ฒด๋ฅผ ๋๊ธธ ์๋ ์๋ค. KeyboardEvent ํ์
์ด KeyboardShortcutEvent ํ์
์ ์ํผ์
(๋ ํฐ ํ์
)์ด๊ธฐ ๋๋ฌธ์ 37๊ฐ ํ๋กํผํฐ๋ฅผ ๋ชจ๋ ๊ฐ์ง๊ณ ์์ง ์์๋ ํจ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ ์ ์๊ฒ ๋ ๊ฒ์ด๋ค.
window.addEventListener('keydown', (e: KeyboardEvent) => {
const shortcut = getKeyboardShortcut(e); // OK!
if (shortcut) execShortcut(shortcut);
});
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[CS] ์ปดํจํฐ์ ์ค์(Real Number) ํํ - ๊ณ ์ ์์์ , ๋ถ๋ ์์์ , ์ง์ ํ๊ธฐ๋ฒ, ์ ๊ทํ ์ด ์ ๋ฆฌ
[CS] ์ปดํจํฐ์ ์ค์(Real Number) ํํ - ๊ณ ์ ์์์ , ๋ถ๋ ์์์ , ์ง์ ํ๊ธฐ๋ฒ, ์ ๊ทํ ์ด ์ ๋ฆฌ
2024.05.27 -
[Algorithm] ์ ํด๋ฆฌ๋ ์๊ณ ๋ฆฌ์ฆ / ์์ธ์๋ถํด๋ก ์ต์๊ณต๋ฐฐ์ ์ต๋๊ณต์ฝ์ ๊ณ์ฐํ๊ธฐ
[Algorithm] ์ ํด๋ฆฌ๋ ์๊ณ ๋ฆฌ์ฆ / ์์ธ์๋ถํด๋ก ์ต์๊ณต๋ฐฐ์ ์ต๋๊ณต์ฝ์ ๊ณ์ฐํ๊ธฐ
2024.05.26 -
[Algorithm] ๋ ๋ฐ๋จน๊ธฐ ์๊ณ ๋ฆฌ์ฆ / ๋์ ๊ณํ๋ฒ
[Algorithm] ๋ ๋ฐ๋จน๊ธฐ ์๊ณ ๋ฆฌ์ฆ / ๋์ ๊ณํ๋ฒ
2024.05.25 -
[JS] split() ๋ฉ์๋์์ ๋น ๋ฌธ์์ด์ด ์๊ธฐ๋ ์๋ฆฌ
[JS] split() ๋ฉ์๋์์ ๋น ๋ฌธ์์ด์ด ์๊ธฐ๋ ์๋ฆฌ
2024.05.24