// 타입스크립트 코드이지만,
// a의 타입을 명시적으로 지정하지 않은 경우이기 때문에, a는 any로 추론된다
// 함수의 리턴 타입은 number로 추론된다 (NaN도 number의 하나)
function f1(a) {
return a * 26;
}
// 사용자는 a가 any이기 때문에, 사용법에 맞게 문자열을 사용해서 함수를 실행했다
console.log(f1(10)); // 260
console.log(f1('Jinju') + 1); // NaN\
// > f1 함수의 작성자는 올바른 사용법을 사용자에게 전달하지 못했음
타입을 명시적으로 지정하지 않은 경우, 타입스크립트가 추론 중 'any'라고 판단하게 되면 컴파일 에러를 발생시켜 명시적으로 지정하도록 유도한다.
// 매개변수의 타입은 명시적으로 지정했다
// 명시적으로 지정하지 않은 함수의 리턴타입은 number로 추론된다
function f2(a: number) {
if(a > 0) {
return a * 38;
}
}
// 사용자는 사용법에 맞게 숫자형을 사용하여 함수를 실행했다
// 해당 함수의 리턴 타입은 number이기 때문에, 타입에 따르면 이어진 연산을 바로 할 수 있다
// 하지만 실제로는 undefined + 5가 실행되어 NaN이 출력된다
console.log(f2(5)); // 190
console.log(f2(-5) + 5); // NaN
// > undefined가 number안에 포함되어 있기 때문이다
모든 타입에 자동으로 포함되어 있는 null과 undefined를 제거해준다
// strictNullChecks 옵션을 켰을 때
// 명시적으로 지정하지 않은 함수의 리턴 타입은 number | undefined로 추론된다
function f2(a: number) {
if(a > 0) {
return a * 38;
}
}
console.log(f2(-5) + 5); // error Ts2532: Object is possibly 'undefined';
// 매개변수의 타입과 함수의 리턴 타입을 명시적으로 지정했다
// 실제 함수 구현부의 리턴 타입과 명시적으로 지정한 타입이 일치하지 않아 컴파일 에러 발생
// error TS2366
function f3(a: number): number {
if(a > 0) {
return a * 38;
} // else에 대한 부분은 명시되어 있지 않아 error
}
// if가 아닌 경우 return을 직접 하지 않고 코드가 종료된다
// error TS7030: Not all code paths return a value
function f4(a: number) {
if (a > 0) {
return a * 38;
}
}
함수 내에서 모든 코드가 값을 리턴하지 않으면, 컴파일 에러를 발생 시킨다
// JavaScript
function f5(a) {
return `이름은 ${a.name} 이고,
연령대는 ${Math.floor(a.age / 10) * 10} 대 입니다.`;
}
console.log(f5({ name: 'Jinju', age: 26})); // 이름은 Jinju이고, 연령대는 20대입니다.
console.log(f5('Jinju')); // 이름은 undefined이고, 연령대는 NaN대 입니다.
function f6(a: {name: string; age: number}): string {
return `이름은 ${a.name} 이고,
연령대는 ${Math.floor(a.age / 10) * 10} 대 입니다.`;
}
console.log(f5('Jinju')); // error TS2345: Argument of type 'string' is not assinable to parameter of type '{ name: string; age: number; }'.
// ❗ 저 타입이 너무 길다면 타입을 생성해주면 된다 ❗
// interface, typeAlias, class 셋 중 아무거나 써도 가능
interface PersonInterface {
name: string;
age: number;
};
type PersonTypeAlias = {
name: string;
age: number;
};
function f6(a: PersonInterface): string {
return `이름은 ${a.name} 이고,
연령대는 ${Math.floor(a.age / 10) * 10} 대 입니다.`;
}
console.log(f5('Jinju')); // error TS2345: Argument of type 'string' is not assinable to parameter of type 'PersonInterface'.
interface IPerson {
name: string;
age: number;
speak(): string;
};
type PersonType = {
name: string;
age: number;
speak(): string;
}
let perseonInterface: IPereson = {} as any;
let personType: PersonType = {} as any;
personInterface = personType;
personType = personInterface;
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 assinable to parameter of type 'PersonID'. Type 'string' is not assinable to type '{ readonly brand: unique symbol; }'.
// 아무 string이 아닌 PersonID로 치환 된 형식만 들어 갈 수 있음을 보여줌
// 자주 사용되는 방식은 아님
class Duck;
def sound(self):
print u "꽥꽥"
class Dog:
def sound(self):
pring u "멍멍"
def get_sound(animal):
animal.sound()
def main():
bird = Duck()
dog = Dog()
get_sound(bird)
get_sound(dog)
// 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 'number[]' is not assignable to type '[number. number]'. Target requires 2 elements but source may have fewer.
// sub3 타입은 sup3 타입의 서브타입이다
let sub3: [number, number] = [1, 2];
let sup3: number[] = sub3;
sub3 = sup3; // error! Type 'number[]' is not assignable to type '[number, number]'. Target requires 2 elemnets but source may have fewer.
// sub4 타입은 sup4 타입의 서브타입이다
let sub4: number = 1;
let sup4L any = sub4;
sub4 = sup4; // any이므로 가능
// sub5 타입은 sup5 타입의 서브타입이다
let sub5: never = 0 as never;
let sup5: number = sub5;
sub5 = 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'.
// primitive type
let sub7: string = '';
let sup7: string | number = sub7;
// objecet - 각각의 프로퍼티가 대응하는 프로퍼티와 같거나 서브타입이어야 한다
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;
class Person {}
class Developer extends Person {
coding() {}
}
class StartUpDeveloper extends Developer {
burning() {}
}
// Developer를 인자로 받고 Developer를 리턴하는 함수
function tellMe(f: (d: Developer) => Developer) {}
// Developer => Developer 에다가 Developer => Developer를 할당하는 경우
tellMe(function dToD(d: Developer): Developer {
return new Developer();
});
// Developer => Developer 에다가 Person(상위, 슈퍼타입) => Developer 를 할당하는 경우
tellMe(function pToD(d: Person): Developer {
return new Developer();
});
// Developer => Developer 에다가 StartupDeveloper(하위) => Developer를 할당하는 경우
tellMe(function sToD(d: StartupDeveloper): Developer { return new Developer();
});
// StartupDeveloper가 하위이므로 Developer는StartUpDeveloper를 모른다 > 논리적으로 오류
// 하지만 타입스크립트는 이 부분에 대해 사용자에게 선택을 준다
// strict 옵션을 켜지 않으면 에러가 뜨지 않지만 논리적으로는 에러가 발생하는 부분이다
// strictFunctionTypes 옵션을 켜면
// 함수를 할당할 시에 함수의 매개변수 타입이 같거나 슈퍼타입인 경우가 아닌 경우, 에러를 통해 경고한다
type MyStringType = string;
const str = 'world';
let myStr: MyStringType = 'hello';
myStr = str;
// 사실 의미가 없는 코드이긴하다
let person: string | number = 0;
person = 'Jinju';
type StringOrNum = string | number;
let another: StringOrNum = 0;
another = 'Someone';
let person: [string, number] = ['Jinju', 26];
type Person = [string, number];
let another: Person = ['Someone', 30];
type EatType = (food: string) => void;
타입이 타입으로써의 목적이 분명하면 > 인터페이스
그저 별명으로 존재한다면 > type alias
기술적으로 인터페이스와 type alias가 차이가 없는 것은 아님!