💡 tsc는 1. 코드 변환하는 역할 2. 타입검사하는 역할
1과 2가 별개여서 타입검사에서 에러가 나도 코드를 js로 변환할 수 있다
npx tsc --init
: tsconfig.json 생성
npx tsc
: ts파일을 js로 바꾸는 명령어
npx tsc --noEmit
: 타입검사만 하는 명령어. 에디터를 사용하지 않으면 해당 명령어를 계속해줘야함
"allowJs": true
: ts와 js를 동시에 쓸 수 있음
esModuleInterop: true, strict: true
: 필수
"skipLibCheck": true
: 라이브러리는 타입검사하지 말라
"forceConsistentCasingInFileNames": true
: 윈도우에서는 대소문자 지키지 않고 import해도 되는데 리눅스에서는 안된다. 따라서 서버에 올렸을때 문제 될 수 있으므로 윈도우에서도 대소문자 지켜야 되도록 하는 옵션이다
문자열 타입, 숫자타입, 진위(boolean) 타입, , 객체타입, 배열타입, 튜플타입, null, undefined, never, any, unknown, void
const tuple: [string, number] = ["1", 1];
tuple[2] = "hello"; // 에러
tuple.push("hello"); // 이건 잘 됨
const array = []
: 빈배열 선언하면 array를 never[]
로 추론interface A {
talk: ()=> number;
}
const a: A = {
talk: ()=> {
return 3
}
}
// any는 타입검사를 포기해버림.
const b: any = a.talk();
b.play(); // 타입에러는 안나지만 b에 play속성이 없으므로 런타임에서 에러난다.
// unknown은 타입검사를 포기하지 않는다.
const c: unknown = a.talk();
c.talk(); // 'c is typeof unknown' 에러가 난다.
(c as A).talk() // unknown은 나중에라도 타입을 지정할 수 있다.
a. return 값 있으면 안 된다.
b. return 값이 있어도 되나, return 값이 뭐든간에 return 값을 사용하지 않겠다.
return값이 void인 함수-> return값 있으면 안 됨
return값이 void인 method -> return 값 있어도 됨 = return 값을 사용하지 않겠다.
return값이 void인 매개변수 함수 -> return 값 있어도 됨 = return 값을 사용하지 않겠다.
// push는 return값이 number이다.
function forEach(arr: number[], callback: (el: number) => undefined): void;
const target: number[] = [];
forEach([1, 2, 3], (el) => target.push(el)); // 에러난다
function forEach(arr: number[], callback: (el: number) => void): void;
const target: number[] = [];
forEach([1, 2, 3], (el) => target.push(el)); // 에러안난다
undefined는 void에 대입 가능하다. void는 undefined에 대입이 불가능했는데 ts5.1부터는 가능하게 되었다
// undefined는 void에 대입 가능하다.
function a():void{
return undefined;
}
// null은 void에 대입 불가능하다.
function a():void{
return null;
}
// void는 undefined에 대입이 불가능하다. ts 5.1부터는 가능하게 되었다.
function a():undefined{
}
const x: {} = "hello"; // 에러가 나지 않는다.
const y: Object = "hi"; // 에러가 나지 않는다.
const xx: object = "hi"; // 에러난다.
const yy: object = { hello: "world" };
const z: unknown = "hi";
// * unknown = {} | null | undefined
if (z) {
z; // {}로 추론된다. 왜냐면,
} else {
z; // null과 undefined는 else로 가기 때문에
}
extends
라는 예약어를 사용한다.interface IAnimal {
breath: true;
}
interface IHuman extends IAnimal {
think: true;
}
// 상속을 여러번 할 수 있다.
interface IHuman {
talk: true;
}
const ieunsu: IHuman = { breath: true, think: true, talk: true };
interface A {
[key: string]: string;
}
const a: A = { a: "hello", b: "world" };
// 문자열 배열은 string[]으로 선언하는 것이 더 간편하고 타입을 파악하기 편함
interface B {
[key: number]: string;
}
const a: B = ["hello", "world"];
type Person = {
name: string;
age: number;
};
type Developer = {
skill: string;
};
var joo: Person & Developer = {
name: "은수",
age: 25,
skill: "프론트엔드 개발",
};
enum EDirection {
Up = 3,
Down,
Left,
Right,
}
enum EDirection {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right",
}
const ODirection = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
// key 값을 꺼내오고싶을 때
type Key = keyof typeof ODirection; // Key를 "Up" | "Down" | "Left" | "Right"로 추론
// value 값을 꺼내오고 싶을 때
type Value = (typeof ODirection)[keyof typeof ODirection]; // Value를 0 | 1 | 2 | 3 으로 추론
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayMyName() {
console.log(`내 이름은 ${this.name}`);
}
}
const eunsu = new Person('은수', 25);
eunsu.sayMyName()
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayMyName() {
console.log(`내 이름은 ${this.name}`);
}
}
class Developer extends Person {
skill: string;
constructor(name: string, age: number, skill: string) {
**super(name, age);**
this.skill = skill;
}
coding() {
console.log(`${this.skill} is Fun`);
}
}
const eunsu = new Developer("은수", 25, "TypeScript");
eunsu.coding();
class A {
#a: string = "123"; // js의 private
private b: number = 123; // ts의 private
method() {
console.log(this.#a, this.b);
}
}
interface A {
readonly a: string;
b: string;
}
class B implements A {
a: string = "123";
b: string = "world";
}
class C extends B {}
new C().a;
new C().b;
abstract class Car {
color: string;
constructor(color: string) {
this.color = color;
}
start() {
console.log("Start");
}
abstract doSomething(): void;
}
class Bmw extends Car {
constructor(color: string) {
super(color);
}
doSomething() {
console.log("do something!");
}
}
const z4 = new Bmw("black");
function getText<T>(text: T): T {
return text;
}
const getText: <T>(text: T) => T = (text) => {
return text;
};
const getText = function <T>(text: T): T {
return text;
};
// 제네릭의 타입을 string으로 제약한 코드
function embraceEverything<T extends string>(thing: T): T {
return thing;
}
// length 속성을 갖는 타입만 취급
function lengthOnly<T extends { length: number }>(value: T) {
return value.length;
}
type DeveloperKeys = keyof { name: string; skill: string };
function printKeys<T extends keyof { name: string; skill: string }>(value: T) {
console.log(value);
}
<T extends {...}>
<T extends any[]>
<T extends (...args: any) => any>
<T extends abstract new (...args: any)=> any>
function printTextLength<T>(text: T){
console.log(text.length) // 'T' 형식에 'length'속성이 없습니다.
}
function add<T extends string>(x: T, y: T): T {
return x + y;
}
// 'string' 형식은 'T' 형식에 할당할 수 없습니다
const result = "Hello" + " World";
// result의 타입은 "Hello World"가 됨
여기서 result
의 타입은 실제 문자열 값인 "Hello World"
가 된다. 이것은 TypeScript가 리터럴 타입으로 정확하게 타입을 추론하는 특성이다. 따라서 제네릭 타입 T
가 문자열일지라도, +
연산자를 사용하여 문자열을 결합하면 TypeScript는 결과를 string
이 아닌 문자열 리터럴 타입으로 간주한다.