TypeScript
20210510 TypeSciprt 05, CompileOptions(strict 계열), Interface, Type alias
TypeScript05
CompileOptions : strict
- 엄격하게 type check하는 옵션들을 전부다 킴
--noImplicitAny
,--noImplicitThis
,--strictNullChecks
,--strictFunctionTypes
,--strictPropertyInitialization
,--strictBindCallApply
,--alwaysStrict
"strict": {
"description": "Enable all strict type checking options.",
"type": "boolean",
"default": false,
"markdownDescription": "Enable all strict type checking options. See more: https://www.typescriptlang.org/tsconfig#strict"
},
--noImplicitAny
- 명시적이지 않게 any 타입을 사용하여, 표현식과 선언에 사용하면, 에러 발생.
// 매개변수에서 error 발생 (any 명시 필요)
function noImplicitAnyTest(arg) {
console.log(arg);
}
- 타입스크립트가 타입 추론에 실패한 경우, any를 의도 했으면 any를 적어라라고 알려줌
- type으로 아무것도 안쓰면 error 발생
- 오류 해결시, any라고 지정되어 있지 않은 경우는 any가 아닌 것임(타입이 추론 되었으니까)
--suppressImplicitAnyIndexErrors
noImplicitAny
사용시, 인덱스 객체에 인덱스 signature가 없는 경우 오류 발생하는 것을 예외처리함 (즉, 허용한다.)
const obj = {
bar: 10,
};
obj["foo"] = 10; // error : 인덱스가 없어서 -> 이것을 허용시켜 줌
obj.baz = 10;
--noImplicitThis
- 명시적이지 않게 any 타입을 사용하여, this 표현식에 사용하면, 에러 발생
- call, apply, bind와 같이 this를 대체하여 함수 콜을 하는 용도로 쓰임
- Class 에서는 this 사용시 noImplicitThis의 에러가 안남
- Class에서는 constructor 를 제외하고 멤버 함수의 첫번째 매개변수도 일반함수처럼 this 사용 가능
// error 예시
function noImplicitThisTest01(name: string, age: number) {
this.name = name; // this error
this. age = age; // this error
return this; // this error
}
// 일반함수의 경우 (TS에서만 사용하는 문법) -> 매개변수 첫번째 자리에 python self처럼 this를 넣어서 타입 지정해야 error가 안 발생
function noImplicitThisTest02(this: any, name: string, age: number) {
this.name = name;
this. age = age;
return this;
}
// Class 에서 this 사용시
class NoImplicitThisTest03 {
private _name: string;
private _age: number;
constructor(name: string, age: number) { // 첫번째 매개변수 this (X)
this._name = name;
this._age = age;
}
public print(this: NoImplicitThisTest03) {
console.log(this._name, this._age);
}
}
new NoImplicitThisTest03('mark', 36).print()
--strictNullChecks
(중요)
- 해당 옵션을 키면 null 및 undifined 값이 모든 유형의 도메인에 속하지 않으며, 그 자신을 타입으로 가지거나, any일 경우에만 할당 가능함
- 예외적으로 undefined만 void 할당 가능
- null, undefined를 가지려면 union으로 사용해야 함 (해당 값을 조건부로 제외하고 사용하는 것이 바람직)
const a: number = null; // error
const b: string = undefined; // error
const c: any = null; // 가능
const d: any = undefined; // 가능
const e: void = undefined; // 가능
--strictFunctionTypes
- 함수 타입에 대한 bivariant 매개변수 검사를 비활성화 함.
- 서브타입의 가능성
- 공변, 반병 -> 반환타입은 공변적, 인자 타입은 반공변적
Animal -> Greyhound , Dog -> Dog
- 매개변수는 같거나 넓어야 되고, return type은 같거나 하위여야 문제가 없음
- typescript 에서는 인자타입은 공변적이면서, 반공변적인게 문제라서 옵션을 켜서 해결
strictPropertyInitialization
- 정의되지 않은 클래스의 속성이 생성자에서 초기화되었는지 확인합니다.
- 이 옵션 사용하려면
--strictNullChecks
를 사용해야 함 - class 내에서 property가 선언만 되고 값이 할당 안되었을 경우 error를 뿜음
class Person01 {
private _name: string; // error
private _age: number; // error
constructor() {} // 할당 안되어 있어서
constructor(name: string, age: number) {
this._name = name;
this._age = age;
} // 이렇게 하면 해결
public print() {
console.log(this._name, this._age);
}
}
// constructor를 사용하지 않는 경우
// (async를 사용하고 싶어서 일반 함수로 initialize 하는 경우)
class Person02 {
private _name!: string;
private _age!: number;
public async initialize(name: string, age: number) {
this._name = name;
this._age = age;
} // 일반 함수로 나중에라도 할당 시키는 경우 (property 선언시 `!`를 붙여서 나중에 해줄거니까 에러 무시해로 사용)
public print() {
console.log(this._name, this._age);
}
}
--strictBindCallApply
- function의 내장함수 bind, call, apply를 사용할때 더 엄격하게 함
- bind는 해당 함수에서 사용할 this와 인자를 설정해주는 역할을 함
- call과 apply는 this와 인자를 설정한 후, 실행 까지 함
- call은 함수의 인자를 여러 인자의 나열로 넣어서 사용
- apply는 모든 인자를 배열 하나로 넣어서 사용함
--alwaysStrict
- 각 소스 파일에 대해 JS의 strict mode로 코드를 분석, 엄격하게 사용을 해제 (에밋 시킴)
- syntex 에러가 ts error로 나오게 됨
- 컴파일 된 js 파일에
use strict
가 추가됨
결론 : strict 는 필수 옵션 임
Interfaces
- interface는 js에 없고 TS에만 존재하는 keyword임
- 컴파일 하면 js파일에서는 없어짐 결국 관계가 맞는지 compile할때 필요함
Interface 사용
interface Person1 {
name: string;
age: number;
}
function hello1(person: Person1): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p1: Person1 = {
name: "mark",
age: 39,
};
hello1(p1);
Optional property 01 (
?
)
- 매개변수에 대한 사용을 필수가 아닌 필요에 따라 쓰고 안쓰고 할 수 있음
?
를 사용해서 age 있어도 되고 없어도 되고?
가 없으면 무조건 필요한 required임
interface Person2 {
name: string;
age?: number;
}
function hello2(person: Person2): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
hello2({ name: "Mark", age: 39 });
hello2({ name: "Anna" });
Optional propety 02 (Indexable type)
?
은 이미 지정해 놓은 특정 매개변수를 쓰고 안쓰고지만,- Indexable type을 이용하면 해당 type에 맞게 여러개를 추가해서 쓸수 있음
interface Person3 {
name: string;
age?: number;
[index: string]: any;
}
function hello3(person: Person3): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p31: Person3 = {
name: "Mark",
age: 39,
};
const p32: Person3 = {
name: "Anna",
sisters: ["Sung", "Chan"], // 할당 받는 값이 any 였으므로 배열도 상관 없음
};
const p33: Person3 = {
name: "Bokman",
father: p31, // 추가 (할당 값 type은 any이므로 상관 X)
mother: p32, // 추가
};
hello3(p32);
Interface를 통한 함수 type 지정과 할당
Interface : function Type 지정하기
- 함수 모양으로 property를 작성함
interface Person4 {
name: string;
age: number;
hello(): void;
}
Interface : function 구현부 할당
- 방법01
- function 키워드를 사용한 방법
const p41: Person4 = {
name: "Tom",
age: 39,
hello: function (): void {
console.log(`안녕하세요! ${this.name} 입니다.`);
},
};
- 방법02
- 함수호출 모양을 사용한 방법
- 만약, 다른 객체를 대상으로 실행하고자 할때는 this 그것도 사용 가능하다는 것을 알려주기 위해서 this: Person4을 함수 첫번째 매개변수에 작성해도 된다.
const p42: Person4 = {
name: "Tom",
age: 39,
hello(): void {
console.log(`안녕하세요! ${this.name} 입니다.`);
},
};
const p42: Person4 = {
name: "Tom",
age: 39,
hello(this: Person4): void {
console.log(`안녕하세요! ${this.name} 입니다.`);
},
};
p41.hello();
p42.hello();
- 주의(Arrow function - this 사용 불가)
- arrow function안에서는 this 사용이 불가함
- 해당 객체를 가르키지 않고 global this를 가르킴 -> 사용 불가
// 잘못된 사용
const p43: Person4 = {
name: "Tom",
age: 39,
hello: (): void => {
console.log(`안녕하세요! ${this.name} 입니다.`); // this error
},
};
Interface를 바탕으로 class 만들기 (implements 키워드) & class를 type처럼 사용하기
Interface를 바탕으로 class 만들기 (implements 키워드)
interface IPerson1 {
name: string;
age?: number;
hello(): void;
}
class Person implements IPerson1 {
name: string;
age?: number | undefined;
constructor(name: string) {
this.name = name;
}
hello(): void {
console.log(`안녕하세요 ${this.name} 입니다.`);
}
}
class를 type처럼 사용하기
// class로 type을 부를 수는 있지만 interface로 부르는게 더 정확하다.
const person12 = new Person("Mark"); // 변수의 type을 명시해줘야 확인하기 좋음
person12.hello();
const person: IPerson1 = new Person("Mark");
person.hello();
Interface : extends inheritance (상속)
interface IPerson2 {
name: string;
age?: number;
}
interface IKorean extends IPerson2 {
city: string;
}
const k: IKorean = {
name: "kim min young",
city: "서울",
};
// overide 가능
interface로 function 표현하기 (이해가 안가는 부분)
- interface를 통해서 interface를 통째로 함수 type형으로 만듦
()
안에 매개변수 type 지정하고,()
밖에 return type을 지정함- 구현체는 type 선언 된 곳과 비교하여 error를 체크함
- 함수 호출 부분은 함수의 구현체 보다 type 선언된 부분을 비교하기 때문에 error가 없음
interface HelloPerson {
(name: string, age?: number): void; // age의 경우 number | undefined 임
}
// 구현체는 type 선언 된 곳과 비교하여 error를 체크함
const helloPerson: HelloPerson = function (name: string) {
console.log(`안녕하세요! ${name} 입니다.`);
};
// const helloPerson: HelloPerson = function (name: string, age: number) {
// console.log(`안녕하세요! ${name} 입니다.`);
// }; // error 할당 불가 하다고 하는데 이해가 안감 ㅠ
helloPerson("Tom", 23);
interface : readonly
- readonly 키워드로 property가 한번 지정되면 변경 불가하게 가능함
interface Person8 {
name: string;
age?: number;
readonly gender: string;
}
// 한번 넣고 바뀌지 않는 값이라면, readonly를 붙여주는 버릇이 필요함
const p81: Person8 = {
name: 'Mark',
gender: 'male',
};
// p81.gender = "female";
// 코드에 의도에 담아서 의사표시를 하기 위해서 TS가 큰 도움을 줌
Type aliase vs Interface
function (함수 type 표현 방법)
- Type alias
type EatType = (food: string) => void;
- Interface
interface IEat {
(food: string): void;
}
array
- Type alias
type PersonList = string[];
- Interface
interface IPersonList {
[index: number]: string;
}
intersection
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtistsData {
artists: { name: string }[];
}
- Type alias
&
기호를 활용해서 표현 가능
type ArtistsResponseType = ArtistsData & ErrorHandling;
- Interface
- 두 개다 상속 받아서(다중 상속) 사용하므로 모두 사용 가능해짐
interface IArtistsResponse extends ArtistsData, ErrorHandling {}
let art: ArtistsResponseType;
let iar: IArtistsResponse;
union types
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
- Type alias
- union type 가능
type PetType = Bird | Fish;
- Interface
- union type 불가능
- union type 상속 받기, class 만들기 모두 불가
interface IPet extends PetType {} // error interface는 오직 object type또는 intersection 만 상속 가능
class Pet implements PetType {} // error class는 object type 또는 intersection만 class로 만들 수 있음
declaration merging - interface
- type alias는 불가
- interface로 type 표현시 같은 이름이면 두개가 합쳐짐
- 기존의 interface에 뭔가 추가하는 경우
interface MergingInterface {
a: string;
}
interface MergingInterface {
b: string;
}
let mi: MergingInterface;
mi.a;
mi.b;
- declaration merging - type alias
- 불가하고, Duplicate error 발생
type MergingType = {
a: string,
};
type MergingType = {
b: string,
};
// Duplicate error 발생
- type alias : type에 이름을 붙여주는 것
- interface : type을 만드는 것이고