노드 설치 명령어
npm i -g typescript
타입스크립트 버전
tsc —version
tsconfig.json 구성하기
tsc —init
tsc 실행
tsc index.ts
타입스크립트의 설계 목표
현재와 미래의 ECMA스크립트 제안에 맞춘다.
모든 자바스크립트 코드의 런타임 동작을 유지한다.
자바스크립트 보다 느리다?
- 소해의 소지가 있음.
- 운영 프레임워크 대다수는 타입스크립트의 컴파일러를 전혀 사용하지 않습니다. 트랜스 파일을 위한 별도의 도구를 사용하고 타입스크립트는 타입 검사용으로만 사용
- 타입 스크립트는 코드를 빌드하는데 시간이 조금 더 걸립니다. 타입스크립트 코드는 브라우저나 Node.js 같은 환경에서 실행되기 전에 자바스크립트로 컴파일되어야 함
- 빌드 파이프라인은 대부분 성능 저하를 무시하도록 설정됨
- 코드에서 발생할 수 있는 오류를 분석하는 타입스크립트 기능은 실행 가능한 애플리케이션 코드 파일을 생성하는 것과는 분리된 채로 수행됨
타입 시스템
타입이란?
‘타입’은 자바스크립트에서 다루는 값의 형태에 대한 설명입니다. 여기서 ‘형태’란 값에 존재하는 속성과 메서드 그리고 내장되어 있는 typeof 연산자가 설명하는 것을 의미.
7가지 원시 타입
- null
- undefined
- boolean
- string
- number
- bigint // 0n, 2n, -4n
- symbol // Symbol(), Symbol(”hi”)
타입스크립트에서는 일반적으로 boolean과 number처럼 소문자로 참조하는 것이 모범 사례
타입 시스템
타입스크립트의 타입 시스템은 다음과 같이 동작함
- 코드를 읽고 존재하는 모든 타입과 값을 이해합니다.
- 각 값이 초기 선언에서 가질 수 있는 타입을 확인합니다.
- 각 값이 추후 코드에서 어떻게 사용될 수 있는지 모든 방법을 확인합니다.
- 값의 사용법이 타입과 일치하지 않으면 사용자에게 오류를 표시합니다.
예시
let firstName = “whitney”;
firstName.length()
- 코드를 읽고 firstName이라는 변수를 이해
- 초기값이 whitney이므로 firstName이 string타입이라고 결론지음
- firstName의 length 멤버를 함수처럼 호출하는 코드를 확인
- string의 .length 멤버는 함수가 아닌 숫자라는 오류를 표시함, 즉 함수처럼 호출할 수 없음
오류 종류
2가지 오류가 있음.
- 구문 오류 : 타입스크립트가 자바스크립트로 변환되는 것을 차단한 경우
- 타입 오류 : 타입 검사기에 따라 일치하지 않는 것이 감지된 경우
구문 오류
구문 오류는 타입스크립트가 코드로 이해할 수 없는 잘못된 구문을 감지할 때 발생합니다. 이는 타입스크립트가 타입스크립트 파일에서 자바스크립트 파일을 올바르게 생성할 수 없도록 차단.
타입 오류
타입 오류는 타입스크립트의 타입 검사기가 프로그램의 타입에서 오류를 감지했을 때 발생합니다. 오류가 발생했다고 해서 타입스크립트 구문이 자바스크립트로 변환되는 것을 차단하지는 않습니다. 하지만 코드가 실행되면 무언가 충동하거나 예기치 않게 작동할 수 있음을 나타냅니다.
할당 가능성
동일한 타입의 다른 값이 할당될 때는 문제가 없습니다.하지만 다른 타입의 값이 할당되면 타입 오류가 발생합니다.
타입 스크립트에서 함수 호출이나 변수에 값을 제공할 수 있는지 여부를 확인하는 것을 할당 가능성이라고 합니다.
할당 가능성 오류 이해하기
Type..is not assignable to type.. 형태의 오류는 타입스크립트 코드를 작성할 때 만나게 되는 가장 일반적인 오류 중 하나
타입 애너테이션
타입스크립트가 읽어야 할 초기값이 없는 경우도 있음. 타입스크립트는 나중에 사용할 변수의 초기 타입을 파악하려고 시도하지 않고 any라는 타입으로 간주함 (세상의 모든 타입)
진화하는 any : 초기 타입을 유추할 수 없는 변수
let rocker // any
rocker = "Joan Jett" // String
rocker.toUpperCase();
rocker = 19.58 // number으로 진화
rocker.toPrecision(1);
rocker.toUpperCase(); // does not exist on type 'number'
일반적으로 any 타입을 사용해 any 타입으로 진화하는 것을 허용하게 되면 타입스크립트의 타입 검사 목적을 부분적으로 쓸모없게 만듦
타입 애너테이션 : 초깃값을 할당하지 않고도 변수의 타입을 선언
let rocker: string,
rocker = "Joan Jett";
불필요한 타입 애너테이션
let firstName: string = "Tina"; // 불필요한 중복
let firstName: string = 42 // 호환되지 않는 경우
타입 형태
타입 스크립트는 변수에 할당된 값이 원래 타입과 일치하는지 외에도 더 많은 것을 수행함
let rapper = "queen latifah"
rapper.push("!"); // push does not exist on type string
모듈
- 모듈 : export 또는 import가 있는 파일
- 스크립트 : 모듈이 아닌 모든 파일
- 타입스크립트는 파일이 스크립트면 전역 스코프로 간주하므로 모든 파일의 내용에 접근 가능
- 타입스크립트는 모듈 간 변수의 이름이 충돌되어 타입 오류가 발생할 수 있음
- 즉 스크립트 파일에 선언된 변수는 다른 스크립트 파일에 선언된 변수와 동일한 이름을 가질 수 없습니다.
유니언과 리터럴
타입스크립트가 해당 값을 바탕으로 추론을 수행하는 두 가지 핵심
- 유니언 union : 값에 허용된 타입을 두 개 이상의 가능한 타입으로 확장하는 것
- 내로잉 narrowing: 값에 허용된 타입이 하나 이상의 가능한 타입이 되지 않도록 좁히는 것
즉 “코드 정보에 입각한 추론”이 가능한 타입스크립트
유니언 타입
let mathematician = Math.random() > 0.5 ? undefined : "Mark Goldberg"; // undefined or string
- 유니언 : “이거 혹은 저거” 타입, 어떤 타입인지 정확히 모르지만 두 개 이상의 옵션 중 하나
- 수직선 연산자(|)를 사용해 두 가지 타입을 가질 수 있음을 표시함
유니언 타입 선언
let thinker: string | null = null; // 현재 null이지만 string값으로 할당 가능
thinker = "Susanne Langer";
유니언 속성
타입스크립트는 유니언으로 선언한 모든 가능한 타입에 존재하는 멤버 속성에 접근할 수 있습니다.
유니언 외 타입으로 접근하려고 하면 타입 검사 오류가 발생합니다.
let physicist = Math.random() > 0.5 ? "Marie Curie" : 84;
physicist.toString(); // Ok
physicist.toUpperCase(); // toUpperCase does not exist on type 'number'
physicist.toFixed(); // toFixed does not exist on type 'string'
- 두 타입 모두 존재하는 toString()은 사용할 수 있지만
- 각각 존재하는 toUpperCase()와 toFixed는 사용할 수 없습니다.
유니언 타입으로 정의된 여러 타입 중 하나의 타입으로 된 값의 속성을 사용하려면 코드에서 값이 보다 구체적인 타입 중 하나라는 것을 타입스크립트에 알려야 합니다. 이 과정을 내로잉이라고 부릅니다.
내로잉
내로잉은 값이 정의, 선언 혹은 이전에 유추된 것보다 더 구체적인 타입임을 코드에서 유추하는 것
값의 타입이 이전보다 더 좁혀졌다는 것을 알게 되면 값을 더 구체적인 타입으로 취급합니다.
타입을 좁히는 데 사용할 수 있는 논리적 검사를 타입 가드라고 합니다.
두 가지 타입 가드
값 할당을 통한 내로잉
let a: number | string;
a = "Grace Hopper";
a.toUpperCase(); // Ok
a.toFixed(); // toFixed does not exist on type 'string';
조건 검사를 통한 내로잉
if 문을 통해 변수의 값을 좁히는 방법
// scientist: number | string
let scientist = Math.random() > 0.5 ? "Rosalind F" : 51;
if (scientist === "Rosalind F") {
// string 타입
scientist.toUpperCase();
}
// scientist: number | string 타입
scientist.toUppderCase(); // toUpperCase does not exist on type 'string | number'
typeof 검사를 통한 내로잉
- typeof 검사는 타입을 좁히기 위해 자주 사용하는 실용적인 방법
리터럴 타입
- 리터럴 타입은 좀 더 구체적인 버전의 원시 타입
const philosopher = "Hypatia";
- 얼핏 봐도 string 타입
- 하지만 단지 string 타입이 아닌 “Hypatia”라는 특별한 값, 더 구체적인 “Hypatia”
- 이것이 바로 리터럴 타입의 개념
- 원시 타입 값 중 어떤 것이 아닌 특정 원싯값으로 알려진 타입이 리터럴 타입니다.
- boolean, null, undefined 타입 외에 number, string과 같은 모든 원시타입에 무한한 리터럴 타입이 존재
- 유니언 타입 애너테이션에서는 리터럴과 원시 타입을 섞어서 사용할 수 있습니다.
let lifespan: number | "ongoing" | "uncertain";
lifespan = 89; // Ok
lifespan = "ongoing"; // OK
// 'true' is not assignable to type number | ongoing | uncertain
lifespan = true//
리터럴 할당 가능성
- number와 string과 값은 서로 다른 원시 타입은 서로 할당되지 못함
- 0과 1처럼 동일한 원시 타입일지라도 서로 다른 리터럴 타입은 서로 할당할 수 없음
let specificallyAda: "Ada";
specificallyAda = "Ada";
specificallyAda = "Byron"; // error
let someString = "";
specificallyAda = somString; // error
someString = ":)"; // Ok
엄격한 null 검사
리터럴로 좁혀진 유니언의 힘은 타입스크립트에서 엄격한 null 검사라 부르는 타입 시스템 영역인 잠재적으로 정의되지 않은 undefined 값으로 작업할 때 특히 두드러집니다.
십억 달러의 실수
십억 달러의 실수는 타입이 필요한 위치에서 null 값을 사용하도록 허용하는 많은 타입 시스템
const first: String = null;
타입 스크립트 컴파일러 옵션 중 하나인 strictNullChecks는 null 검사 활성화 여부 결정
- 비활성화하면 null or undefined를 할당할 수 있음
- 비활성화하면 발생할 수 있는 문제는 어떤게 있을까?
let nameMaybe = Math.random() > 0.5 ? "Tony" : undefined;
nameMaybe.toLowwerCase();
타입스크립트는 잠재적인 충돌을 확인합니다.
엄격한 null 검사를 활성화해야만 코드가 null 또는 undefined 값으로 인한 오류로부터 안전한지 파악
참 검사를 통한 내로잉
truthy : && 연산자는 if 문처럼 boolean 문에서 true로 간주, falsy를 제외한 모든 값은 참
fasly : false, 0, -0, .n, “”, null, undefined, NaN
타입스크립트는 잠재적인 값 중 truthy로 확인된 일부에 한해서만 변수의 타입을 좁힐 수 있습니다.
string | undefined 타입에서 undefined는 항상 falsy이므로 타입스크립트는 if문의 코드 블록에서 string타입이 되어야 한다고 추론할 수 있음
let geneticist = Math.random() > 0.5 ? "Barbara McClintock" : undefined;
if (geneticist) {
geneticist.toUpperCase(); // Ok
}
geneticist.toUpperCase(); // undefined
논리 연산자인 &&와? 는 참 여부를 검사하는 일도 수행가능함. 하지만 그 외에 기능은 제공하지 않음
geneticist && geneticist.toUpperCase(); // Ok
geneticist?.toUpperCase(); // Ok
let biologist = Math.random() > 0.5 && "Rachel Carson";
if (biologist) {
biologist; // 타입 : String
} else {
biologist; // 타입 : false | string
}
초기값이 없는 변수
- 자바스크립트에서는 초깃값이 없는 변수는 기본적으로 undefined입니다.
- 타입스크립트에서는 undefined를 포함하지 않는 타입으로 변수를 선언한 다음에 값을 할당하기 전에 사용하려고 하면 오류 메시지가 발생함
let mathematician: string | undefined;
mathematician?.length; //Ok
mathematician = "Mark Goldberg";
mathematician.length; // Ok
타입 별칭 (type alias)
type MyName = ~~;
type t = boolean | number | string | null | undefined;
let rowData1: t;
let rowData2: t;
let rowData3: t;
타입 별칭은 자바스크립트가 아니다.
타입 별칭은 타입 애너테이션처럼 자바스크립트로 컴파일되지 않습니다.
따라서 런타임 코드에서 참조할 수 없습니다. 타입스크립트는 런타임에 존재하지 않은 항목에 접근하려고 하면 타입 오류로 알려줍니다. (즉 개발 시에만 존재)
타입 별칭 결합
타입 별칭은 다른 타입 별칭을 참조할 수 있습니다. 유니언 타입인 타입 별칭 내에 또 다른 유니언 타입인 타입 별칭을 포함하고 있다면 다른 타입 별칭을 참조하는 것이 유용합니다.
type a = number | string;
type b = a | undefined | null; // number | string | undefined | null
- 꼭 순서대로 작성할 필요는 없다. a와 b가 바뀌어도 됨
객체 리터럴은 각자의 타입이 있는 키와 값의 집합입니다.
객체 타입
- {…} 구문을 사용해서 객체 리터럴을 생성하면, 타입스크립트는 해당 속성을 기반으로 새로운 객체 타입 또는 타입 형태를 고려합니다.
- 해당 객체 타입은 객체의 값과 동일한 속성명과 원시타입을 갖는다.
- 속성에 접근하려면 value.멤버 or value[’멤버’]를 사용
const poet = {
born: 1935,
name: "Mary Oliver",
}
poet['born']
poet.name;
객체 타입 선언
객체 타입은 객체 리터럴과 유사하게 보이지만 필드 값 대신 타입을 사용해 설명함
let poeatLater: {
born: number;
name: string;
};
//ok
poeatLater: {
born: 1935,
name: "Mary Oliver",
};
poeatLater = "Sappho" // error
별칭 객체 타입
객체 타입을 계속 작성하는 일은 매우 귀찮다.
각 객체 타입에 타입 별칭을 할당해 사용하는 방법이 더 일반적임
type Poet = {
born: number;
name: string;
}
let poetLater: Poet;
//Ok
poetLater = {
born: 1935,
name: "Sara Teasdale",
};
poetLater = "Emily Dickinson";
- 대부분의 타입스크립트는 객체 타입을 정의할 때 interface키워드를 사용하는 것을 선호함
구조적 타이핑
- 타입스크립트의 타입 시스템은 구조적으로 타입화 되어있음
- 즉, 타입을 충족하는 모든 값을 해당 타입의 값으로 사용할 수 있음
- 다시 말하자면 매개변수나 변수가 특정 객체 타입으로 선언되면 타입 스크립트에 어떤 객체를 사용하든 해당 속성이 있어야 한다고 말해야 함
type WithFirstName = {
firstName: string;
};
type WithLastName = {
lastName: string;
};
const hasBoth = {
firstName: "Lucille",
lastName: "Clifton",
};
// Ok
let withFirstName: WithFirstName = hasBoth;
let withLastName: WithLastName = hasBoth;
구조적 타이핑은 덕 타이핑과 다릅니다.
덕 타이핑은 ‘오리처럼 보이고 오리처럼 꽥꽥거리면, 오리일 것이다’라는 문구에서 유래
- 타입 스크립트의 타입 검사기에서 구조적 타이핑은 정적 시스템이 타입을 검사하는 경우입니다.
- 덕 타이핑은 런타임에서 사용될 때까지 객체 타입을 검사하지 않는 것을 말합니다.
요약하면 자바스크립트는 덕 타입인 반면 타입스크립트는 구조적으로 타입화 됩니다.
사용 검사
객체 타입으로 애너테이션된 위치에 값을 제공할 때 타입스크립트는 값을 해당 객체 타입에 할당할 수 있는지 확인합니다. 할당하는 값에는 객체 타입의 필수 속성이 있어야 합니다. 없다면 타입오류 발생
type FirstAndLastNames = {
first: string;
last: string;
};
// OK
const hasBoth: FirstAndLastNames = {
first: "Sarojini",
last: "Naidu",
};
const hasOnlyOne: FirstAndLastNames = {
// Error: Property 'last' is missing
first: "Sappho"
};
// 타입이 일치하지 앟는 경우도 허용되지 않음
type TimeRange = {
start: Date,
};
const hasStartString: TimeRange = {
start: "1879-02-13",
// Error: Type 'string' is not assignable to type 'Date'
};
초과 속성 검사
변수가 객체 타입으로 선언되고, 초깃값에 객체 타입에서 정의된 것보다 많은 필드가 있다면 타입스크립트에서 타입 오류가 발생합니다.
type Poet = {
born: number;
name: string;
}
const extraProperty: Poet = {
//Error
activity: "walking",
born: 1935,
name: "Mary Oliver",
}
초과 속성 검사는 객체 타입으로 선언된 위치에서 생성되는 객체 리터럴에 대해서만 일어납니다.
기존 객체 리터럴을 제공하면 초과 속성 검사를 우회합니다.
const existingObject = {
activity: "walking",
born: 1935,
name: "Mary Oliver",
};
const extraPropertyButOk: Poet = existingObject; // Ok
중첩된 객체 타입
자바 스크립트 객체는 다른 객체의 멤버로 중첩될 수 있으므로 타입스크립트의 객체 타입도 타입 시스템에서 중첩된 객체 타입을 나타낼 수 있어야 합니다. 이를 구현하는 구문은 이전과 동일하지만 기본 이름 대신에 {…} 객체 타입을 사용합니다.
type Author = {
firstName: string;
lastName: string;
};
type Poem = {
author: Author;
name: string;
};
const poemMismatch: Poem = {
author: {
name: "Sylvia Plath",
// Error
},
name: "Tulips",
};
선택적 속성
모든 객체에 객체 타입 속성이 필요한 건 아닙니다. 타입의 속성 애너테이션에서 : 앞에 ?를 추가하면 선택적인 속성임을 나타낼 수 있습니다.
“Book 타입은 pages 속성만 필요하고 author 속성은 선택적으로 허용한다.”
type Book = {
author?: string;
pages: number;
};
const ok: Book = {
author: "Rita Dove",
page: 80,
};
const missing: Book = {
author: "Rita Dove",
// Error
};
?를 사용해 선택적으로 선언된 속성은 존재하지 않아도 됩니다. 필수로 선언된 속성과 | undefined는 그 값이 undefined일지라도 반드시 존재해야 합니다.
type Writers = {
author: string | undefined;
editor?: string;
};
// Ok
const hasRequired: Writers = {
author: undefined,
};
const missingRequired: Writers = {};
객체 타입 유니언
유추된 객체 타입 유니언
변수에 여러 객체 타입 중 하나가 될 수 있는 초깃값이 주어지면 타입스크립트는 해당 타입을 객체 타입 유니언으로 유추합니다. 유니언 타입은 가능한 각 객체 타입을 구성하고 있는 요소를 모두 가질 수 있습니다. 객체 타입에 정의된 각각의 가능한 속성은 비록 초깃값이 없는 선택적(?) 타입이지만 각 객체 타입의 구성 요소로 주어집니다.
const poem = Math.random() > 0.5
? { name: "The Double Image", pages: 7}
: { name: "Her Kind", rhymes: true};
poem값은 항상 string 타입인 name 속성을 가지고 pages와 rhymes는 있을 수도 있고 없을 수도 있다.
명시된 객체 타입 유니언
type PoemWithPages = {
name: string;
pages: number;
};
type PoemWithRhymes = {
name: string;
rhymes: boolean;
};
type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() > 0.5
? { name: "The Double Image", pages : 7 }
: { name: "Her Kind", rhymes: true };
poem.name;
// Error pages does not exist on type Poem
// Propery 'pages' does not exist on type 'PoemWithRhymes',
poem.pages;
// error rhymes does not exist on type Poem
// Propery 'rhymes' does not exist on type 'PoemWithRhymes',
poem.rhymes;
객체 타입 내로잉
코드에서 객체의 형태를 확인하고 타입 내로잉이 객체에 적용
if( "pages" in poem) {
poem.pages;
} else {
poem.rhymes;
}
위 코드는 가능하지만, 타입스크립트는 if (poem.pages)와 같은 형식으로 참 여부를 확인하는 것을 허용하지 않습니다.
판별된 유니언
유니언 타입으로 된 객체의 또 다른 인기 있는 형태는 객체의 속성이 객체 형태를 나타내도록 하는 것입니다. 이러한 타입 형태를 판별된 유니언이라 부르고, 객체의 타입을 가리키는 속성이 판별값입니다.
교차 타입
타입스크립트 유니언 타입은 둘 이상의 다른 타입 중 하나의 타입이 될 수 있음을 나타냅니다.
자바스크립트의 런타입 | 연산자가 & 연산자에 대응하는 역할을 하는 것처럼, 타입스크립트에서도 &교차 타입을 사용해 여러 타입을 동시에 나타냅니다.
type Artwork = {
genre: string;
name: string;
};
type Writing = {
pages: number;
name: string;
};
type WrittenArt = Artwork & Writing;
교차 타입의 위험성
교차 타입은 유용한 개념이지만, 스스로나 타입스크립트 컴파일러를 혼동시키는 방식으로 사용하기 쉽습니다. 교차 타입을 사용할 때는 가능한 한 코드를 간결하게 유지해야 합니다.
- 긴 할당 가능성 오류 : 읽기 힘든 오류메시지
- never : 프로그래밍 언어에서 never 타입은 bottom 타입 또는 empty 타입을 뜻합니다. bottom 타입은 값을 가질 수 없고 참조할 수 없는 타입이므로 bottom 타입에 그 어떠한 타입도 제공할 수 없습니다.
type NotPossible = number & string;
Reference
러닝 타입스크립트