νμ μ€ν¬λ¦½νΈμ ꡬ쑰μ νμ΄ν
μ€λ¬΄μμ νμ μ€ν¬λ¦½νΈλ₯Ό 1λ μ λ λ€λ£¨λ©° λ κΉμ μ΄ν΄μ νμμ±μ λλΌλ μ°Έμ 'μ΄νν°λΈ νμ μ€ν¬λ¦½νΈ'λΌλ μ± μ μ½κ² λκ³ μ€μ©μ μΈ λ΄μ©μ΄ λ§μ μ 리ν΄λλ €κ³ ν©λλ€.
μ± μλ νμ μ€ν¬λ¦½νΈμ νΉμ± μ€ νλμΈ 'ꡬ쑰μ νμ΄ν'μ΄λΌλ κ°λ μ΄ μμ£Ό λ±μ₯ν©λλ€.
μλ°μ€ν¬λ¦½νΈκ° λ νμ΄ν1 (duck typing) κΈ°λ°μ΄κ³ νμ μ€ν¬λ¦½νΈκ° μ΄λ₯Ό λͺ¨λΈλ§νκΈ° μν΄ κ΅¬μ‘°μ νμ΄νμ μ¬μ©ν¨μ μ΄ν΄ν΄μΌ ν©λλ€. νμ μ 'λ΄μΈ'λμ΄ μμ§ μμ΅λλ€.
β μμ΄ν 4. ꡬ쑰μ νμ΄νμ μ΅μν΄μ§κΈ°
ꡬ쑰μ νμ΄νμ κ°μ₯ κΈ°λ³Έμ μΈ νΉμ±μ κ°μ ν λΉν λ μ μλ νμ μ νμν μμ±μ κ°μ§κ³ μλ€λ©΄ νΈνλλ€λ μ μ λλ€.
type Person = { name: string }; let person: Person; const employee = { name: 'Sanghyeon', job: 'Developer', }; person = employee; // OK
employee
λ Person
νμ
μ νμν name
μμ±μ κ°μ§κ³ μκΈ° λλ¬Έμ κ·Έ μΈμ μμ±μ΄ μλλΌλ person
μ κ°μΌλ‘ ν λΉν μ μμ΅λλ€.
('μμ¬ μμ± μ²΄ν¬'λΌλ μμΈκ° μκ³ , μλμμ μ΄ν΄λ³Ό μμ μ
λλ€)
νμ μ κ°μ μ§ν©
νμ μ€ν¬λ¦½νΈλ λ νμ μ μ΄λ»κ² λΉκ΅νκ³ ν λΉ κ°λ₯ μ¬λΆλ₯Ό νλ¨ν κΉμ?
νμ μ κ°μ μ§ν©μΌλ‘ μκ°νλ©΄ μ΄ν΄νκΈ° νΈν©λλ€(νμ μ 'λ²μ').
β μμ΄ν 7. νμ μ΄ κ°λ€μ μ§ν©μ΄λΌκ³ μκ°νκΈ°
νμ
μ κ°μ μ§ν©μΌλ‘ λ³Έλ€λ©΄ number
λ 1, 2, ..., n κ³Ό κ°μ 무ν μ§ν©μ΄ λκ³ , boolean
μ true
, false
λ₯Ό κ°μΌλ‘ κ°λ μ ν μ§ν©μ΄ λ©λλ€.
μλ‘ λ€λ₯Έ νμ
μ ν©μ union(|
)μΌλ‘ μ μν μ μμ΅λλ€. κ·Έλ¦¬κ³ κ²ΉμΉλ νμ
μ intersection(&
)μΌλ‘ μ μλ©λλ€. κ²ΉμΉλ νμ
μ΄ μλ κ²½μ° κ³΅μ§ν©μ΄ λ©λλ€.
type AB = 'a' | 'b'; type BC = 'b' | 'c'; type CD = 'c' | 'd'; type ABC = AB | BC; // 'a' | 'b' | 'c' type B = AB & BC; // 'b' type EMPTY = AB & CD; // never
νμ μ μ§ν©μ λμν΄λ³΄λ©΄ λ€μ νμ κ°μ΅λλ€.
νμ μ€ν¬λ¦½νΈ μ©μ΄ | μ§ν© μ©μ΄ |
---|---|
never | 곡μ§ν© |
1 , a λ±μ 리ν°λ΄ | μμκ° 1κ°μΈ μ§ν© |
T1μ΄ T2μ ν λΉ κ°λ₯ | T1μ΄ T2μ λΆλΆ μ§ν© |
T1 | T2 (Union) | T1κ³Ό T2μ ν©μ§ν© |
T1 & T2 (Intersection) | T1κ³Ό T2μ κ΅μ§ν© |
μμ νμ λ°λ₯΄λ©΄ μ΄λ€ νμ T1μ΄ T2μ ν λΉ κ°λ₯νλ €λ©΄ T1μ΄ T2μ λΆλΆ μ§ν©μ΄μ΄μΌ ν©λλ€.
'a'
λΌλ κ°μ'a' | 'b'
νμ μ λ²μμ ν¬ν¨λ©λλ€. λ°λΌμ ν λΉ κ°λ₯ν©λλ€.[1, 2]
λΌλ κ°μnumber[]
νμ μ λ²μμ ν¬ν¨λ©λλ€. λ°λΌμ ν λΉ κ°λ₯ν©λλ€.
μ¬κΈ°κΉμ§ μ°Έ μ½μ΅λλ€. κ·Έλ°λ° μμ λμ¨ Person
κ°μ²΄μ μμ μ λμ
νλ©΄ μ μ λ¨Έλ¦¬κ° λ³΅μ‘ν΄μ§λλ€.
name
κ³Ό job
μμ±μ κ°μ§ employee
λ name
μμ±λ§ κ°μ§ Person
μ ν λΉ κ°λ₯ν©λλ€. μ¦, employee
νμ
μ Person
μ λΆλΆ μ§ν©μ΄λΌλ μλ―Έκ° λ©λλ€.
μ λ μ΄ λΆλΆμ΄ μ°Έ μ΄ν΄κ° λμ§ μμμ΅λλ€. μ μΆκ° μμ±μ κ°μ§ κ°μ²΄κ° λΆλΆ μ§ν©μ΄ λλ건μ§?
μ€μ λ‘ κ°μ²΄ νμ μ μ°μ°κ³Ό μΆλ‘ μ μλμ κ°μ΄ λμν©λλ€.
type Person = { name: string; }; type Employee = { job: string; }; function union(input: Person | Employee) { input.name // ~~~~ Property 'name' does not exist on type 'Person | Employee'. input.job // ~~~~ Property 'job' does not exist on type 'Person | Employee'. } function intersection(input: Person & Employee) { input.name // string input.job // string }
μ λ μ λ¬Έλ²μ μ νμ λ μμ±μ κΈ°μ€μΌλ‘ μ λ°λμ κ²°κ³Όλ₯Ό μμνμ΅λλ€.
νμ μ°μ°μλ μΈν°νμ΄μ€μ μμ±μ΄ μλ, κ°μ μ§ν©(νμ μ λ²μ)μ μ μ©λ©λλ€. κ·Έλ¦¬κ³ μΆκ°μ μΈ μμ±μ κ°μ§λ κ°λ μ¬μ ν κ·Έ νμ μ μν©λλ€.
β μμ΄ν 7. νμ μ΄ κ°λ€μ μ§ν©μ΄λΌκ³ μκ°νκΈ°
μ± μμλ μμ κ°μ΄ μ€λͺ λμ΄ μμ΄, μ κ°λ μ λ°λ‘ μ‘κΈ° μν΄ κ°μ μ§ν©μΌλ‘ νμ μ°μ°νλ λ°©λ²μ κ³ λ―Όν΄λ΄€μ΅λλ€.
Person
νμ
μ ν λΉ κ°λ₯ν κ°μ μ§ν©:
{ name: 'Sanghyeon' } { name: 'Sanghyeon', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μΈ `name` νλλ₯Ό κ°μ§ κ°μ²΄
Employee
νμ
μ ν λΉ κ°λ₯ν κ°μ μ§ν©:
{ job: 'Developer' } { job: 'Developer', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μΈ `job` νλλ₯Ό κ°μ§ κ°μ²΄
Person | Employee (ν©μ§ν©)
:
μ λμ¨ μ°μ°μΌλ‘ κ°μ μ§ν©μ λͺ¨λ λͺ¨μ보면 νμ μ‘΄μ¬νλ νλκ° μλ κ²μ νμΈν μ μμ΅λλ€.
{ name: 'Sanghyeon' } { name: 'Sanghyeon', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } { job: 'Developer' } { job: 'Developer', extra: 'value' } { name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' } κ·Έ μΈ `name` νλλ₯Ό κ°μ§ κ°μ²΄ κ·Έ μΈ `job` νλλ₯Ό κ°μ§ κ°μ²΄
Person & Employee (κ΅μ§ν©)
:
μΈν°μΉμ
μ°μ°μΌλ‘ 곡ν΅λ κ°μ μ§ν©μ μΆλ €λ³΄λ©΄ name
, job
νλκ° νμ μ‘΄μ¬νλ κ²μ νμΈν μ μμ΅λλ€.
{ name: 'Sanghyeon', job: 'Developer' } { name: 'Sanghyeon', job: 'Developer', extra: 'value' }
μμ κ°μ΄ μ μ΄λ³΄λ λκ° μ΄ν΄κ° λ λ― λ§λ― ν©λλ€. μμμΌλ‘λ νλ² μμλ³΄κ² μ΅λλ€. μκ³ μ§λ΄λ λλͺ¨λ₯΄κ° λ²μΉ2μ μ μ©ν΄ μλμ κ°μ΄ μ¨ λ³Ό μ μμ΅λλ€.
keyof (Person | Employee) = (keyof Person) & (keyof Employee) = 'name' & 'job' // never (곡μ§ν©) keyof (Person & Employee) = (keyof Person) | (keyof Employee) = 'name' | 'job' // 'name' | 'job'
(μ± μ λμ¨ λ΄μ©μ΄κΈ΄ νμ§λ§) λκ°λ₯Ό μ¦λͺ ν΄λΈ κΈ°λΆμ λλ€! μ΄μ κ°μ²΄ νμ μ°μ°μ μ΄λ μ λ μ΄ν΄κ° λμ΅λλ€.
ꡬ쑰μ νμ΄ν κ΄μ μ extends
νμ
μ€ν¬λ¦½νΈμμ extends
λ μΈν°νμ΄μ€μ νμ₯ λλ μ λ€λ¦μ νμ μλ‘ μ°μ
λλ€.
extends
λμμ μ΄ν΄νκΈ° μν΄ μλμ κ°μ΄ μΈν°νμ΄μ€μ νμ₯λ μΈν°νμ΄μ€ κ°μ κ΄κ³λ₯Ό μ΄ν΄λ³΄κ² μ΅λλ€.
interface Person { name: string; } interface Employee extends Person { job: string; } const person1: Person = { name: 'Sanghyeon' }; const employee1: Employee = { name: 'Sanghyeon', job: 'Developer' }; let person2: Person; let employee2: Employee; person2 = employee1; // OK employee2 = person1; // ~~~ Property 'job' is missing in type 'Person' but required in type 'Employee'.
Person
μ Employee
μ ν λΉν μ μμ΅λλ€. λ°λ©΄ Employee
λ Person
μ ν λΉ κ°λ₯ν©λλ€.
μμ λμ¨ μ§ν©μ λμλ νμ λ°λ₯΄λ©΄ Employee
λ₯Ό Person
μ ν λΉ κ°λ₯νλ€λ κ²μ Employee
κ° Person
μ λΆλΆ μ§ν©μ΄ λλ€λ κ²μ λ»ν©λλ€.
μ΄μ A extends B
λ₯Ό 보면 Aλ Bμ λΆλΆ μ§ν©μ΄λ€λ₯Ό λ μ¬λ¦΄ μ μμ΅λλ€.
μ΄ κ°λ
μ μ΅νλ μ λ€λ¦μ extends
λ μ΄ν΄κ° λμ΅λλ€.
interface Person { name: string; } function sort<P extends Person>(people: P[]): P[] { return people.sort((a, b) => a.name > b.name ? 1 : -1); }
sort
ν¨μμ μΈμλ Person
νμ
μ ν λΉ κ°λ₯ν νμ
μ λ°°μ΄μ΄μ΄μΌ ν©λλ€.
κ·Έλμ μ λ€λ¦ μ μΈμμμ extends
λ λ²μλ₯Ό νμ νλ μΌμ’
μ 쑰건문μΌλ‘ λμνκ² λ©λλ€.
μμ¬ μμ± μ²΄ν¬ (Excess Property Check)
ꡬ쑰μ νμ΄ν κ΄μ μμ μμΈ μΌμ΄μ€κ° μμ΅λλ€.
νμ μ΄ λͺ μλ λ³μμ κ°μ²΄ 리ν°λ΄μ ν λΉν λ νμ μ€ν¬λ¦½νΈλ ν΄λΉ νμ μ μμ±μ΄ μλμ§, κ·Έλ¦¬κ³ 'κ·Έ μΈμ μμ±μ μλμ§' νμΈν©λλ€.
β μμ΄ν 11. μμ¬ μμ± μ²΄ν¬μ νκ³ μΈμ§νκΈ°
let person: Person; person = { name: 'a', job: 'b', // ~~~~~~~ Type '{ name: string; job: string; }' is not assignable to type 'Person'. // Object literal may only specify known properties, and 'job' does not exist in type 'Person'. };
μ§κΈκΉμ§ λ΄€λ μμ μ λ¬λ¦¬ κ°μ²΄ 리ν°λ΄μ μ¦μ ν λΉνλ μμ±μ μ격νκ² μ²΄ν¬νλ κ²μ νμΈν μ μμ΅λλ€. μμ¬ μμ± μ²΄ν¬λ₯Ό μ μ©νκ² μΈ μ μλ μΌμ΄μ€κ° λͺ κ°μ§ μμ΅λλ€.
μ νμ (optional) μμ±μ μ€ν κ²μ¦
type Person = { name: string; job?: string; } let person: Person; const personWithTypo = { name: 'sh', jop: 'abc', }; person = personWithTypo; // OK? person = { name: 'sh', jop: 'abc', // ~~ Object literal may only specify known properties, // but 'jop' does not exist in type 'Person'. Did you mean to write 'job'? };
λ³μλ₯Ό μ μΈν λ€ ν λΉν λλ μ‘μ§ λͺ»νλ jop
μ΄λΌλ μ νμ μμ±μ μ€νλ₯Ό μ‘μλΌ μ μμ΅λλ€.
κ°λ
μ±μ μν΄ λ³μλ‘ μΆμΆν΄ 미리 ννν΄λλ κ²½μ°κ° λ§μλ° μμ£Ό λ³νλ κ°μ²΄μΈ κ²½μ° μ€μλ₯Ό λ°©μ§νκΈ° μν μλ¨μΌλ‘ νμ©ν΄λ΄λ μ’μ κ² κ°μ΅λλ€.
맀νλ νμ μ μ¬μ©ν΄ κ°μ λκΈ°ννκΈ°
μΈν°νμ΄μ€μ μλ‘μ΄ μμ±μ μΆκ°ν λ, μ νμ κ°μ νλλ‘ λ§€νλ νμ μ κ³ λ €ν΄μΌ ν©λλ€.
β μμ΄ν 18. 맀νλ νμ μ μ¬μ©νμ¬ κ°μ λκΈ°ννκΈ°
React μ»΄ν¬λνΈ μ΅μ ν κΈ°λ² μ€ props
μ λ³νλ₯Ό μ격νκ² μ²΄ν¬ν΄ νμν κ²½μ°μλ§ λ λλ§νλ κΈ°λ²μ΄ μμ΅λλ€.
props
λ₯Ό λ°μ§ μκ±°λ μμ νμ
μΌλ‘ ꡬμ±λ κ²½μ° React.memo
λ₯Ό κ°μΈλ κ² λ§μΌλ‘ λμμ΄ κ°λ₯ν©λλ€.
νμ§λ§ μμ±κ³Ό κ°μ΄ μμ£Ό λ³νλ κ²½μ°λΌλ©΄ λ λ²μ§Έ μΈμμΈ μ¬μ©μ μ μ λΉκ΅ ν¨μ(custom comparison function)λ‘ μ μ΄ν΄μΌ ν©λλ€.
μ΄ λ μμ¬ μμ± μ²΄ν¬ κΈ°λ²μΌλ‘ λΉκ΅ ν¨μμ μΈμμ props
μμ±μ λκΈ°ν ν μ μμ΅λλ€.
type Props = { x: number; y: number; radius: number; onClick: VoidFunction; }; function Point(props: Props) { return (...); } const REQUIRES_UPDATE: { [k in keyof Props]: boolean } = { x: true, y: true, radius: true, onClick: false, }; export default React.memo(Point, (prevProps, nextProps) => { let k: keyof Props; for (k in prevProps) { if (REQUIRES_UPDATE[k] && prevProps[k] !== nextProps[k]) { // μ λ°μ΄νΈ λμ μμ±μ΄κ³ κ°μ΄ λ¬λΌμ§ κ²½μ° λ€μ λ λλ§νκΈ° return false; } } return true; });
REQUIRES_UPDATE
κ°μ²΄λ κ°μ΄ λ³ν λ μ
λ°μ΄νΈν΄μΌ νλ νλλ₯Ό boolean κ°μΌλ‘ κ΄λ¦¬ν©λλ€.
νμ
μ μμ λμμ κ°μ²΄ 리ν°λ΄λ‘ ν λΉνκΈ° λλ¬Έμ μμ¬ μμ± μ²΄ν¬ λμμ΄ λ©λλ€.
λ§μ½ ν¬μΈνΈλ₯Ό 그릴 λ μμμ΄ νμν΄μ‘λ€κ³ κ°μ ν΄λ³΄λ©΄, μλμ κ°μ΄ props
νμ
μ λ³κ²½νκ² λ κ²μ
λλ€.
type Props = { x: number; y: number; radius: number; color: string; // μλ‘μ΄ μμ± μΆκ° onClick: VoidFunction; }; const REQUIRES_UPDATE: { [k in keyof Props]: boolean } = { // ~~~~~~~~~~~~~~~ Property 'color' is missing in type '{ x: true; y: true; radius: true; onClick: false; }' // but required in type '{ x: boolean; y: boolean; radius: boolean; color: boolean; onClick: boolean; }'. x: true, y: true, radius: true, onClick: false, // `useCallback`μ΄ μ μ©λ ν¨μλΌλ©΄ λΉκ΅ κ°λ₯ };
μ΄ λ νμ
μ€ν¬λ¦½νΈλ REQUIRES_UPDATE
κ°μ²΄μ μλ‘μ΄ μμ±μ μ
λ°μ΄νΈ λμ μ¬λΆκ° μ€μ λμ§ μμλ€κ³ μλ €μ€λλ€.
λ§μ½ μ΄ κ³Όμ μ΄ μμλ€λ©΄ color
κ°μ΄ λ°λμ΄λ UIκ° μ
λ°μ΄νΈλμ§ μλ λ²κ·Έκ° μκ²Όμ μλ μμ΅λλ€.
μμ¬ μμ± μ²΄ν¬λ₯Ό ν΅ν΄ λ²κ·Έλ₯Ό λ―Έμ°μ λ°©μ§ν μ μλ μ’μ μ¬λ‘μ
λλ€.
μ€λ¬΄μμ 볡μ‘ν μ»΄ν¬λνΈμ λ λλ§ νμλ₯Ό μ€μ΄κΈ° μν΄ μμ΄μ μΈ λ°©λ²μΌλ‘ μμ± νλνλ 체ν¬νκ±°λ
lodash.isEqual
ν¨μλ‘ κΉμ λΉκ΅(deep equal)λ₯Ό νλ μ μ΄ μμλλ°
μ μμ μ κ°μ΄ μ μν΄λλ©΄ μꡬμ¬νμ΄ λ³ν λ νμ
체ν¬λ‘ λ²κ·Έλ₯Ό λ°©μ§νκ³ , λΉκ΅ μ°μ° λΉμ©μ μ€μΌ μ μμ΅λλ€.
κ·Έλ¦¬κ³ μλμ κ°μ΄ μ λλ¦ κΈ°λ°μ μ νΈ ν¨μλ‘ μμ±ν΄ μ¬μ¬μ© ν μ μμ΅λλ€.
type Requirements<P extends object> = { [k in keyof Required<P>]: boolean; }; const createMemoComparator = <P extends object>( requirements: Requirements<P>, customizer?: (prevProps: P, nextProps: P) => boolean ) => { return (prevProps: P, nextProps: P) => { let k: keyof P; for (k in prevProps) { if (requirements[k] && prevProps[k] !== nextProps[k]) { return false; } } if (typeof customizer === 'function') { return customizer(prevProps, nextProps); } return true; }; }; type Props = { x: number; y: number; radius: number; onClick: VoidFunction; }; function Point(props: Props) { return (...); } export default React.memo(Point, createMemoComparator<Props>({ x: true, y: true, radius: true, onClick: false, });
λ§λ¬΄λ¦¬
νμ
μ€ν¬λ¦½νΈμ ꡬ쑰μ νμ΄νμ κ΄ν΄ μ 리νλ©° κ°μ₯ μ΄λ €μ λ λΆλΆμ νμ
μ°μ°μ μν΄ νμ
μ κ°μ μ§ν©
μ΄λΌλ κ°λ
μ λμ
μν€λ κ³Όμ μ΄μμ΅λλ€.
μ€μ λ‘ νμ 곡μ νμ λ μ΄ λΆλΆμ μ€λͺ
μ΄ νΌλλλ€λ νΌλλ°±μ΄ λ§μμ΅λλ€.
A data type provides a set of values from which an expression (i.e. variable, function, etc.) may take its values
β Data type, Wikipedia
νλ‘κ·Έλλ° μΈμ΄νμ μ μλ λ΄μ©μμλ λμ νμ μΈμ΄μΈ μλ°μ€ν¬λ¦½νΈμ μ΅μν΄μ Έ μλ€ λ³΄λ μμ°μ€λ½κ² λ°μλ€μ΄μ§ λͺ»νκ² λ κ² κ°μ΅λλ€.
νμ μ€ν¬λ¦½νΈμ ν΅μ¬ μ리 μ€ νλμΈ κ΅¬μ‘°μ νμ΄νμ λ€μ νλ² μ 리ν΄λ³΄λ©΄ μλμ κ°μ΅λλ€.
- μλ°μ€ν¬λ¦½νΈλ₯Ό λͺ¨λΈλ§νκΈ° μν΄ κ°μ ννλ₯Ό κΈ°μ€μΌλ‘ ν λΉ μ¬λΆλ₯Ό κ²°μ μ§λ νΉμ±μ κ°μ§
- νμ μ μ μλμ§ μμ μμ±κ³Ό κ°μ΄ ν¬ν¨λ μ μλ€λ μ μ μ μν΄μΌ ν¨
- κ°μ κΈ°μ€μΌλ‘ νλ¨νκΈ° λλ¬Έμ ν μ€νΈ μμ± μ mock κ°μ²΄λ₯Ό νμ©νκΈ° μ¬μ
- μμ¬ μμ± μ²΄ν¬λ ꡬ쑰μ νμ΄νμ μμΈ μΌμ΄μ€λ‘ κ°μ²΄ 리ν°λ΄μ ν λΉν λ λΆνμν μμ±μ΄λ μ€νλ₯Ό νμΈν¨
Footnotes
-
λ νμ΄νμ΄λ, κ°μ²΄κ° μ΄λ€ νμ μ λΆν©νλ λ³μμ λ©μλλ₯Ό κ°μ§ κ²½μ° κ°μ²΄λ₯Ό ν΄λΉ νμ μ μνλ κ²μΌλ‘ κ°μ£Όνλ λ°©μμ λλ€. "λ§μ½ μ΄λ€ μκ° μ€λ¦¬μ²λΌ κ±·κ³ , ν€μμΉκ³ , κ½₯κ½₯거리λ μ리λ₯Ό λΈλ€λ©΄ λλ κ·Έ μλ₯Ό μ€λ¦¬λΌκ³ λΆλ₯Ό κ²μ΄λ€." β©
-
λ λͺ¨λ₯΄κ°μ λ²μΉ - μν€λ°±κ³Ό, μ°λ¦¬ λͺ¨λμ λ°±κ³Όμ¬μ β©