1-1.타입 시스템 이란?
1-2.타입스크립트의 타입 시스템
1-3.자신의 코드에서 해당 함수를 사용하는 사용자 vs 해당 함수를 구현하는 구현자
- 타입이란 해당 변수가 할 수 있는 일을 결정합니다.
// f1 이라는 함수의 body 에서는 a를 사용할 것 입니다. // a가 할 수 있는 일은 a의 타입이 결정합니다. function f1(a) { return a; }
- 함수 사용법에 대한 오해를 야기하는 자바스크립트
// JavaScript // (f2 실행의 결과가 NaN 을 의도한 것이 아니라면) // 이 함수의 작성자는 매개변수 a 가 number타입이라는 가정으로 함수를 작성했습니다. function f2(a) { return a * 38; } // 사용자는 사용법을 숙지하지 않은 채, 문자열을 사용하여 함수를 실행했습니다. console.log(f2(10); // 380 console.log(f2('Mark')); // NaN
- 타입스크립트의 추론에 의지하는 경우
// 타입스크립트 코드지만, // a의 타입을 명시적으로 지정하지 않은 경우이기 때문에 a는 any로 추론됩니다. // 함수의 리턴 타입은 number로 추론됩니다. (NaN도 number의 하나입니다.) function f3(a) { return a * 38; } // 사용자는 a가 any이기 때문에, 문자열을 사용하여 함수를 실행했습니다. console.log(f3(10); // 380 console.log(f3('Mark') + 5); // NaN
1-4.noImplicitAny
옵션
noImplicitAny
옵션을 켜면 타입을 명시적으로 지정하지 않은 경우, 타입스크립트가 추론 중 any
라고 판단하게 되면, 컴파일 에러를 발생시켜 명시적으로 지정하도록 유도한다.
nolmplicitAny
에 의한 방어// error Ts7006: Parameter 'a' implicitly has an 'any' type. function f3(a) { return a * 38; } // 사용자의 코드를 실행할 수 없습니다. 컴파일이 정상적으로 마무리 될 수 있도록 수정해야합니다. console.log(f3(10)); console.log(f3('Mark') + 5);
- 함수의 매개변수에 타입을 명시적으로 지정하지 않았기 때문에 컴파일 에러 메시지가 출력된다.
number
타입을 추론된 리턴 타입// 매개변수의 타입은 명시적으로 지정했습니다. // 명시적으로 지정하지 않은 함수의 리턴 타입은 number로 추론됩니다. function f4(a: number) { if (a > 0) { return a * 38; } } // 사용자는 사용법에 맞게 숫자형을 사용하여 함수를 실행했습니다. // 해당 함수의 리턴 타입은 number이기 때문에, 타입에 따르면 이어진 연산을 바로 할 수 있습니다. // 하지만 실제 undefined + 5 가 실행되어 NaN 이 출력됩니다. console.log(f4(5)); // 190 console.log(ft(-5) + 5); // NaN
- 매개변수
a
값이0
보다 크지 않으면 리턴하지 않기 때문에undefined
값이 리턴됩니다.- 함수의 매개변수에만 타입을 지정하고 함수의 리턴 타입은 따로 지정하지 않았으나 명시적으로 지정하지 않은 함수의 리턴 타입은
number
로 추론됩니다.TypeScript
에서는number
안에undefined
가 포함되어져 있습니다.
1-5.strictNullChecks 옵션
strictNullChecks
옵션을 켜면 모든 타입에 자동으로 포함되어 있는 null
과 undefined
를 제거해줍니다.
number
|undefined
타입으로 추론된 리턴 타입// 매개변수의 타입은 명시적으로 지정했습니다. // 명시적으로 지정하지 않은 함수의 리턴 타입은 number | undefined 로 추론됩니다. function f4(a: number) { if (a > 0) { return a * 38; } } // 사용자는 사용법에 맞게 숫자형을 사용하여 함수를 실행했습니다. // 해당 함수의 리턴 타입은 number | undefined 이기 때문에, // 타입에 따르면 이어진 연산을 바로 할 수 없습니다. // 컴파일 에러를 고쳐야하기 하기 때문에 사용자와 작성자가 의논을 해야합니다. console.log(f4(5)); console.log(f4(-5) + 5); // error TS2532: Object is possibly 'undefined'.
1-6.명시적으로 리턴 타입을 지정해야 할까?
TS
가 봐줍니다.// 매개변수의 타입과 함수의 리턴 타입을 명시적으로지정했습니다. // 실제 함수 구현부의 리턴 타입과 명시적으로 지정한 타입이 일치하지 않아 컴파일 에러가 발생합니다. // error TS2366: Function lacks ending return statement and return type does not inc function f5(a: number): number { if (a > 0) { return a * 38; } // if 가 아닌 부분은 작성이 덜 된 것 같다고 에러 발생 }
1-7.noImplicitReturns 옵션
noImplicitReturns
옵션을 켜면 함수 내에서 모든 코드가 리턴 값을 리턴하지 않으면 컴파일 에러를 발생시킨다.// if 가 아닌 경우 return을 직접 하지 않고 코드가 종료된다. // error TS7030: Not all code paths return a value. function f5(a: number) { if (a > 0) { return a * 38; }
- 매개변수에
object
가 들어오는 경우// JavaScript function f6(a) { return `이름은 ${a.name} 이고, 연령대는 ${ Math.floor(a.age / 10) * 10 }대 입니다.`; } console.log(f6({ name: 'Mark', age: 38 })); // 이름은 Mark이고, 연령대는 30대 입니다. console.log(f6('Mark')); // 이름은 undefined이고, 연령대는 NaN대 입니다.
JS
코드에서 매개변수a
에object
타입이 아니더라도 에러 메시지가 출력되지않고 잘못된 결과가 출력되는 문제 발생
- object literal type
function f7(a: {name: string; age: number): string { return `이름은 ${a.name} 이고, 연령대는 ${ Math.floor(a.age / 10) * 10 }대 입니다.`; } console.log(f7({ name: 'Mark', age: 38 })); // 이름은 Mark이고, 연령대는 30대 입니다. console.log(f6('Mark')); // error TS2345: Argument of type 'string' is not // assignable to parameter of type '{ name: string; age: number; }'.
TS
코드에서 매개변수a
에object
를 정확히 명시적으로 지정하여 잘못된 값이 매개변수에 입력되면 에러 메시지 출력
1-8.나만의 타입을 만드는 방법 (TS 주요 기능)
// 예시 interface PersonInterface { name: string; age: number; } type PersonTypeAlias = { name: string; age: number; }; function f8(a: PersonInterface): string { return `이름은 ${a.name} 이고, 연령대는 ${ Math.floor(a.age / 10) * 10 }대 입니다.`; } console.log(f8({ name: 'Mark', age: 38 })); // 이름은 Mark이고, 연령대는 30대 입니다. consoel.log(f8('Mark')); // error TS2345: Argument of type 'string' is not // assignable to parameter of type 'PersonInterface'.
2-1.structural type system
TS
)interface IPerson { name: string; age: number; speak(): string; } type PersonType = { name: string; age: number; speak(): string; }; let personInterface: IPerson = {} as any; let personType: PersonType = {} as any; persontInterface = personType; personType = personInterface;
2-2.nominal type system
C
, JAVA
와 유사)type PersonID = string & { readonly brand: unique symbol }; function PersonID(id: string): PersonID { return id as PersonID; } function getPersonByID(id: PersonID) {} getPersonById(PersonID('id-aaaaaa')); getPersonById('id-aaaaaa'); // error TS2345: Argument of type 'string' is not // assignable to parameter of type 'PersonID'. Type 'string' is not assignable to type // '{ readonly brand: unique symbol; }'.
2-3.duck typing (duck testing에서 유래)
Method
들을 확인하여 타입을 정한다.Duck Typing
)이다.TS
는 duck typing
이 아닙니다. (Python
과 유사)class Duck: def sound(self): print u"꽥꽥" class Dog: def sound(self): print u"멍멍" def get_sound(animal): animal.sound() def main(): bird = Duck() dog = Dog() get_sound(bird) get_sound(dog)
3-1.서브 타입1
// sub1 타입은 sup1 타입의 서브 타입이다. let sub1: 1 = 1; let sup1: number = sub1; sub1 = sup1; // error! Type 'number' is not assignable to type'1'. // sub2 타입은 sup2 타입의 서브타입이다. let sub2: number[] = [1]; let sup2: object = sub2; sub2 = sup2 // error! Type '{}' is missing the following properites form type // 'number[]': length, pop, push, concat, and 16 more // sub 3 타입은 sup3 타입의 서브 타입이다. let sub3: [number, number] = [1, 2]; let sup3: numbr[] = sub3; sub3 = sup3; // error! type 'number[]' is not assignable to type '[number, number]'. // Target requires 2 element(s) but source may have fewer.
3-2.서브 타입2
// sub4 타입은 sup4 타입의 서브 타입이다. let sub4: number = 1; let sup4: any = sub4; sub4 = sup4; // sub5 타입은 sup5 타입의 서브 타입이다. let sub5: never = 0 as never; let sup5: number = sub5; sub 5 = sup5 // error! Type 'number' is not assignable to type 'never'. class Animal {} class Dog extends Animal { eat() {} } // sub6 타입은 sup6 타입의 서브 타입이다. let sub6: Dog = new Dog(); let sup6: Animal = sub6; sub6 = sup6; // error! Property 'eat' is missing in type 'SubAnimal' but // required in type 'SubDog'.
3-3.공변 - 같거나 서브 타입인 경우, 할당이 가능하다.
// primitive type let sub7: string = ''; let sup: string | number= sub7; // object - 각각의 프로퍼티가 대응하는 프로퍼티와 같거나 서브타입이어야 한다. let sub8: { a: string; b: number } = { a: '', b: 1}; let sup8: { a: string | number; b: number } = sub8; // array - object 와 마찬가지 let sub9: Array<{ a: string; b: number }> = [{ a: '', b: 1 }]; let sup9: Array<{ a: string | number; b: number }> = sub8;
- 일반적으로
sub
는 좁은 범위의 타입이고sup
는 넓은 범위의 타입이다.sub
는sup
범위 안에 들어갈 수 있지만sup
는sub
범위안에 들어가면 에러가 발생한다.
3-4.반병 - 함수의 매개변수 타입만 같거나 슈퍼타입인 경우, 할당이 가능하다.
class Person {} class Developer extends Person { coding() {} } class StartupDeveloper extends Developer { burning() {} } // tellme 함수 생성 function tellme(f: (d: Developer) => Developer) {} // tellme(Developer => Developer) 에다가 Developer => Developer 를 할당하는 경우 tellme(function dToD(d: Developer): Developer { return new Developer(); }); // tellme(Developer => Developer) 에다가 Person => Developer 를 할당하는 경우 (슈퍼 타입) tellme(function pToD(d: Person): Developer { return new Developer(); }); // tellme(Developer => Developer) 에다가 StartupDeveloper => Developer 를 할당하는 경우 (서브 타입 => 옵션 활성화시 에러 발생) tellme(function sToD(d: StartupDeveloper): Developer { return new Developer(); }); // 사용자에게 선택옵션을 줌. (옵션을 켜지 않으면 융통성을 발휘해서 에러가 발생하지 않음) // strictFunctionTypes 옵션을 켜면 에러를 통해 경고한다.
strictFunctionTypes
옵션을 켜면 함수를 할당할 시에 함수의 매개변수 타입이 같거나 슈퍼타입인 경우가 아닌 경우, 에러를 통해 경고한다.- 기본적으로
TS
는 타입 호환성 규칙으로 공변성을 갖고 있지만 함수의 매개변수는 반공변성을 갖고 있습니다.
type alias
는 특정 타입이나 인터페이스를 참조할 수 있는 타입 변수를 의미합니다.interface
처럼 변수명 앞에 type
이라는 표현을 사용합니다.Interface
와 기능적으로 유사해 보이지만 다릅니다.refer
(별명)로 사용하는 것이지 타입을 만드는 것은 아닙니다.Primitive
, Union Type
, Tuple
, Function
4-1.Aliasing Primitive
type alias
에는string
이나number
등의 기본 타입을 부여할 수 있다.- 기본적인 방식대로 변수에 직접 타입을 선언하면 다음과 같다.
const shoeSize: number = 270;
type alias
를 사용하면 이렇게 표현할 수 있다.type Size = number; // number 타입으로 Size 변수 생성! const shoeSize: Size = 270;
4-2.Aliasing Union Type
- 기본적인 방식대로 변수에 직접 타입을 선언하면 다음과 같다.
let person: string | number = 0; person = 'Mark';
type alias
를 사용하면 이렇게 표현할 수 있다.type StringOrNumber = string | number; let another: StringOrNumber = 0; another = 'Anna'; // 유니온 타입은 A도 가능하고 B도 가능한 타입
4-3.Aliasing Tuple
- 기본적인 방식대로 변수에 직접 타입을 선언하면 다음과 같다.
let person: [string, number] = ['Mark', 35];
type alias
를 사용하면 이렇게 표현할 수 있다.type PersonTuple = [string, number]; let another: PersonTuple = ['Anna', 24]; let another2: PersonTuple = ['Top', 22]; // 튜플 타입에 별칭을 줘서 여러군데서 사용할 수 있다.
4-4.Aliasing Function
type EatType = (food: string) => void;
4-5.
interface
와의 비교
- 4-5-1.선언 방식
interface Book { title: string; author: string; totalPage: number; isbn: number; } type Book = { title: string; author: string; totalPage: number; isbn: number; }
type alias
를 사용할 때는 등호(=
)를 사용!
- 4-5-2.개념적 차이
interface
는string
,number
처럼 아예 그 자체를 새로운 타입으로서 만드는 방식이다.- 그러나
type
은 선언한 타입들을 변수처럼 단순히 참조하기 위해 이름을 부여한 것이다.
- 4-5-3.확장성
type alias
는 선언한 이후에 확장이 불가능하다.- 이에 반해
interface
는extends
표현식을 사용해 확장할 수 있다.- 이러한 확장성 측면에서, 타입을 선언할 때
type
보다는interface
를 사용하는 것이 권장된다.
- 4-5-4.
interface
와Alias
를 혼용해서 쓸 때 구분하는 법- 타입으로서의 목적이나 존재가치가 명확하면
interface
사용- 다른대상을 가리킨다거나 별명으로서만 존재한다면
Alias
사용