Javascript
└ 매우 유연해서 에러를 잘 보여주지 않음
숫자 배열 + false → 배열이 사라지고 string으로 바뀜
함수의 인자가 잘못 들어가도 실행됨→ return값이 NaN일 뿐, 에러가 나지 않음
const a = { a: "A" };
a.hello();
실행 시 에러 발생 →실행 전에 에러 감지 불가
Typescript
└ 타입 안정성 → 버그 감소
타입스크립트 코드 테스트
https://www.typescriptlang.org/play
타입스크립트 핸드북
https://typescript-kr.github.io/pages/basic-types.html
타입스크립트 = Strongly typed programming
1.TypeScript는 JavaScript에 추가적인 구문을 추가하여 editor와의 단단한 통합을 지원합니다. editor에서 초기에 오류를 잡을 수 있습니다.
TypeScript 코드는 JavaScript가 실행되는 모든 곳(브라우저, Node.js 또는 Deno 및 앱 등)에서 JavaScript로 변환가능
TypeScript는 JavaScript를 이해하고 타입 추론(type inference)을 사용하여 추가 코드 없이도 훌륭한 도구를 제공
타입스크립트가 제공하는 보호장치는 타입스크립트 코드가 자바스크립트로 변환되기 전 발생함
타입스크립트가 먼저 우리코드 확인 후 변환된 자바스크립트안에서 ㅏㅂ보 같은 실수가 일어나지않게 확인해줌
타입스크립트가 에러가 날 것 같은 코드가 있으면 자바스크립트로 아예 컴파일 되지 않음
let a: boolean = "x" → 🚫 boolean 타입에 string타입 할당 불가 알림
JavaScript is a type safe langauge. No
What happens if we run this code in JS: It will run
What is a runtime error? An error that happens while the code runs
What is Typescript compiled to? JavaScript Code
We always have to specify a type for our variables in Typescript.
No, Typescript can infer it sometimes
How do we set the type of a variable explicitly in TS?
const x : string = "hi"
let a :number[] =[1,2]
===
let a = [1,2]
const player: {
name:string, age? number}= {
name:"nico"
}
player는 object type, name은 string이고 age는 number이거나 undefined라고 되어있음
type Age = number;
type Name = string;
type Player={
name:Name,
age?:Age}
cont nico: Player={
name:"nico"}
function playerMaker(name:string){
return {
name
}
}
const nico = playerMaker("nico")
const playerMaker = (name:string) : Player => ({name})
const nico = playerMaker("nico")
readOnly
JavaScript에서는 mutability(변경 가능성)이 기본값이지만 타입스크립트에서는 readonly를 통해 읽기 전용으로 만들 수 있습니다.
interface Pizza {
readonly x: number;
}
let pizza: Pizza = { x: 1 };
pizza.x = 12; // error
Tuple
정해진 개수와 순서에 따라 배열 선언
const player: [string, number, boolean] = ["nico", 1, true]
any: 아무 타입, typeScript의 모든 보호장치를 비활성화 시킴
undefined: 선언X 할당X
null: 선언O 할당X
TypeScript에서 중요한건 TypeCheckerd와의 소통
Unknown
변수의 타입을 미리 알지 못 할 때 unknwon을 사용함
let a :unknown;
if(typeof a === 'number'){
let b = a+1
}
if(typeof a === "string"){
let b = a. toUpperCase();}
void
아무것도 return하지않는 함수를 대상으로 사용
function hello(){
console.log('x')}
never
함수가 절대 return하지 않을 때 발생
return하지 않고 오류를 발생시키는 함수
function hello():never{
return "x"
}
// 에러
function hello():never{
throw new Error("xxx")
}
What is the syntax to type an array of booleans?
: boolean[]
For what do we use Type Aliases?
To create a new name for a type
What is the syntax to say that a function returns an array of strings?
function name(): string[]
Does the readonly from Typescript compile to JavaScript?No
Why do we use Tuples for?
To specify an array with a min. length and type positions
We should try to use any as much as possible.No
What happens when we use any? We escape the TS world
What do we have to do before using a variable of type unknown?
We have to first check with typeof
When do we use void? When a function does not return anything
When do we use never? When a path of code should never run
type Add = (a:number, b:number) => number;
const add:Add = (a,b) => a+b
오버로딩은 함수가 서로 다른 여러개의 call signatures를 가지고 있을 때 발생시킴
type Config = {
path:string, state:object}
type Push = {
(path:string):void
(config:Config):void
}
const push:Push = (config) => {
if(typeof config === "string") {console.log(config)}
else{
console.log(config.path)
}
}
What is call signature? Is the type of the arguments and return value of a function
A call signature has the implementation of the function.No
Call Signatures will be compiled into Javascript No
We can use the same call signature for multiple functions. Yes
When does 'overloading' happen? When a function has multiple call signatures
When should we use function overloading? When the function should accept different argument types
geneiric이란, 타입의 placeholder 같은거임
우리는 typescript로 placeholder를 작성할 수 있고,
그게 뭔지 추론해서 함수 사용
generic 사용법
type SuperPrint = {<TypePlaceholder>(arr:TypePlaceholder[]):TypePlaceholder}
const superPrint:SuperPrint = (arr) => arr[0]
const a = superPrint([1,2,3,4]) // const superPrint: <number>(arr: number[]) => void
const b = superPrint([true, false, true])
<>안에 이름 맘대로
type NicoExtra = { favFood:string;}
type NicoExtra = Player<{favFood:string}>
type NicoPlayer = Player<{NicoExtra}>
type Player<E> = { name: string extraInfo:E}
// Last
type Last = <T>(items: T[]) => T;
const last: Last = (items) => items[items.length - 1];
const lastItem = last([1, 2, 3, 4, 5]);
console.log(lastItem);
// Prepend
type Prepend = <T>(items: T[], item: T) => T[];
const prepend: Prepend = (items, item) => [item, ...items]
const items = [1, 2, 3, 4, 5];
const newItems = prepend(items,0);
console.log(newItems)
class Player {
constructor(
private firstName:string,
private lastName:string,
public nickname;string
){}
}
const nico = new Player("nico", "las", "니꼬");
nico.firstName 이런식으로 쓰면 컴파일 x 왜냐 private이니까
private keyword는 오로지 typeScript가 너를 보호해주기 위해서만 사용하는 것
abstract class User{
constructor(
private firstName:string,
private lastName:string,
public nickname;string
){}
abstract getNickName():void
protected getFullName(){
return `${this.firstName} ${this.lastName}`
}
}
class Player extends User{
getNickName(){
console.log(this.nickname)
}
}
const nico = new Player("nico", "las", "니꼬");
nico.getFullName()
추상클래스는 오직 다른곳에서 상속만 받을 수 있는 클래스
추상메소드는 네가 추상 클래스를 상속받는 모든것들이 구현을 해야하는 메소드를 의미
type Words = { // 해시
[key: string]: (string | string[])
//객체의 property에 대해 모르지만 타입만을 알 때 유용하다
}
class Dict {
private words: Words
constructor() {
this.words = {}
}
add(word: Word) { // word는 Word 클래스의 인스턴스 타입.
if(!this.words[word.term]) {
// 사전에 없는 단어이면
this.words[word.term] = word.def;
}
}
find(term: string) {
return this.words[term];
}
// 단어를 삭제
rmv(term: string) {
delete this.words[term];
}
// 단어 이름 업데이트
update(oldTerm: string, newTerm: string) {
if(this.words.hasOwnProperty(oldTerm)) {
this.words[newTerm] = this.words[oldTerm];
delete this.words[oldTerm];
}
}
// 사전에 저장된 단어의 개수
size() {
return Object.keys(this.words).length;
}
// 모든 사전의 이름과 뜻 출력
all() {
for(let [key, value] of Object.entries(this.words)) {
console.log(`${key}: ${value}`)
}
}
}
// words는 initializer 없이 선언해주고 contructor에서 수동으로 초기화
// constructor에 인자로 넣어 constructor가 지정해주길 바라는 게 아니므로
// 각각의 단어에 대한 클래스
class Word {
constructor(public term: string, public def: (string| string[])) {}
// 단어 출력하는 메소드
toString() {
console.log(`${this.term}: [뜻] ${this.def}`);
}
// 단어 정의 추가
addDef(newDef : string) {
if(typeof this.def === 'string') {
this.def = [this.def, newDef]
} else {
this.def = [...this.def, newDef];
}
}
// 단어 정의 수정
updateDef(oldDef : string, newDef: string) {
if(typeof this.def === 'string') {
if(oldDef === this.def) this.def = newDef
} else {
this.def.filter(val => val !== oldDef);
this.def.push(newDef);
}
}
}
/** 출력 */
const kimchi = new Word("kimchi", "한국의 음식");
const tang = new Word("연근 갈비탕", "중국의 음식");
const sushi = new Word("스시", "일본의 음식");
kimchi.addDef("고춧가루로 배추를 버무려 숙성 및 발효시킨 음식")
kimchi.toString(); // kimchi: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
tang.toString() // 연근 갈비탕: 중국의 음식
sushi.updateDef("일본의 음식", "밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식");
sushi.toString(); // 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
const dict = new Dict();
dict.add(kimchi);
dict.add(tang);
dict.add(sushi);
dict.all()
// kimchi: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
// 연근 갈비탕: 중국의 음식
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
dict.find("kimchi");
// (2) ['한국의 음식', '고춧가루로 배추를 버무려 숙성 및 발효시킨 음식']
dict.size()
// 3
dict.update("kimchi", "김치")
dict.all()
// 연근 갈비탕: 중국의 음식
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
// 김치: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
dict.rmv("연근 갈비탕");
dict.all()
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
// 김치: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
type Words = {
[key: string]: string;
};
class Dict {
private words: Words;
constructor() {
this.words = {};
}
//추가
add(word: Word) {
if (this.words[word.term] === undefined) {
this.words[word.term] = word.get;
}
}
//정의 반환
get(term: string) {
return this.words[term];
}
//단어 업데이트
update(word: Word) {
if (this.words[word.term] !== undefined) {
this.words[word.term] = word.get;
}
}
//단어 삭제
del(term: string) {
if (this.words[term] !== undefined) {
delete this.words[term];
}
}
//단어 수
count(){
return Object.keys(this.words).length;
}
//모든
showAll() {
return Object.keys(this.words)
}
}
class Word {
constructor(public term: string, public get: string) {}
}
const baseball = new Word("baseball", "ball game on the mound");
const basketball = new Word("basketball", "ball game on the coart");
const dict = new Dict();
dict.add(baseball);
dict.add(basketball);
console.log(dict.get("baseball"));
console.log(dict.get("basketball"));
console.log(`사전 전체 단어 : ${dict.showAll()}`);
dict.update(new Word("basketball", "ball game on the court"));
console.log(dict.get("basketball"));
dict.del("baseball");
console.log(dict.get("baseball"));
console.log(`사전 전체 단어 : ${dict.showAll()}`);
console.log(`사전 전체 단어 갯수: ${dict.count()}`);
type Words = {
[key: string]: string;
};
class Dict {
private words: Words;
constructor() {
this.words = {};
}
add(term: string, definition: string) {
if (!this.words[term]) {
this.words[term] = definition;
}
}
get(term: string) {
return this.words[term];
}
delete(term: string) {
delete this.words[term];
}
update(term: string, newDef: string) {
if (this.words[term]) {
this.words[term] = newDef;
}
}
showAll() {
Object.keys(this.words).forEach((term) =>
console.log(`${term}: ${this.words[term]}`)
);
}
count() {
return Object.keys(this.words).length;
}
}
const dictionary = new Dict();
dictionary.add("김치", "밋있는 한국 음식");
dictionary.showAll();
console.log(dictionary.count());
dictionary.update("김치", "밋있는 한국 음식!!!");
console.log(dictionary.get("김치"));
dictionary.delete("김치");
console.log(dictionary.count());
type Team = "red" | "blue"| "yellow"
type Health = 1|5|10
typeScript에게 오브젝트의 모양을 알려주는 방법엔 2가지가 있음
type Player = {
nickname:string,
team:Team
health:Health
}
하나는 type을 쓰고, 이렇게 오브젝트의 모양을 써주는 방법이 있고
interface Player {
nickname:string
team:Team
health:Health
}
인터페이스는 타입스크립트에게 오브젝트의 모양을 설명해주는 하나의 목적으로만 사용 가능함
interface User{name:string}
intergace Player extends User{}
const nico:Player = {
name:"nico"
}
protected는 추상 클래스로부터 상속받는 클래스들이 property에 접근하도록 해줌
abstract class User {
constructor (
protected firstName:string,
protected lastName:string
){}
abstract sayHi(name:string):string
abstract fullNmae():string
}
class Player extends User{
fullName(){
return `${this.firstName} ${this.lastName}`
}
sayHi(name:string){
return `Hello ${name}. My name is ${this.fullName()}`
}
}
상속받는 클래스가 어떻게 동작해야할 지 일러주기 위해서 추상클래스를 사용함
추상클래스를 만들면 이게 js에서는 일반적인 클래스로 바뀌어버림
인터페이스는 컴파일하면 JS로 바뀌지 않고 사라짐
인터페이스는 고유한 사용처가 있고 클래스의 모양을 알려준다는 점에서 엄청 유용함
A readonly class property in TS will also be readonly in JS. No
Is this type: string[] the same as Array ? Yes
Is this code correct?
interface Sizes = "xs" | "s" | "m" | "l" | "xl"
No
type Player = {
name:string,
size:"xs" | "s" | "m" | "l" | "xl"
}
YES
What is the difference between a type and an interface?
Interfaces can only be used to type an object, a Type can be used for anytype.
What is the difference between an abstract class and a "normal" class?
We can't make an instance of an abstract class
And abstract class will become a normal class in JS
Yes
When we compile the code interfaces will dissapear. Yes
타입을 쓰고 싶다면 type keyword를 쓰면 됨
Type ver.
type PlayerA= { name : string }
const playerA: PlayerA = {
name:"nico"
}
Interface Ver.
interface PlayerB {
name:string}
const playerB: PlayerB= {name:"nico"}
어케 타입 상속? 또 다른 타입 하나를 만들어서 playAA 타입이 PlayerA 타입과 lastName을 가지는 오브젝트를 합친 거라고 알려줘야함
type PlayerA = {
name:string }
type PlayerAA = PlayerA & {
lastName:string
}
const playerA: PlayerAA = {
name:"nico",
lastName:"xxx"
}
인터페이스 상속 방법
interface PlayerB{name:string}
interface PlayerBB extends PlayerB {lastName:string}
const playerB: PlayerB = {
name:"nico",
lastName:"xxx"
}
Type Aliases과 인터페이스는 매우 유사하며 많은 경우 자유롭게 선택할 수 있음
인터페이스의 거의 모든 기능은 type에서 사용할 수 있으며, 주요 차이점은 type을 다시 열어 새 속성을 추가할 수 없는 것
반면 인터페이스는 항상 확장 가능
Adding new fields to an existing interface
interface Window {title:string}
interface Window {ts:TypeScriptApi}
const src = 'const a = "hello wolrd"';
window.ts.transpilemOdule(src,{});
type Window = {title:string}
type Window={ts:TypeScriptApi}
ERROR!
다형성은 다른 모양의 코드를 가질 수 있께 해주는 것
다형성을 이룰 수 있는 방법은 제네릭을 사용하는거임
제네릭은 placeholder type을 쓸 수 있도록 해줌
concrete type이 아니라 placeholder type
interface LocalStorageAPI<T> {
[key:string]:T
}
class LocalStorage<T> {
private storage:LocalStorageAPI<T> = {}
set(key:string, value:T)
{
this.storage[key] = value;
}
remove(key:string) {
delete this.storage[key]
}
get(key:string):T{
return this.storage[key]
}
clear(){
this.storage={}
}
}
const stringStorage = new LocalStorage<string>
stringStorage.get("hi")
const booleanStorage = new LocalStorage<boolean>();
booleanStorage.get("xxx")
interface GeolocationAPI {
}
때가 되면 타입스크립트가 placeholder type을 concrete type으로 바꾸어 줄거야
이 T인 value를 string인 value로 바꿔주는거임
참고
LocalStorage API
먼저 로컬 스토리지에 들어갈 아이템들의 타입을 설정
이번에는 interface를 사용
interface의 경우 상속이 굉장히 용이한 타입 식별자
로컬 스토리지 내부에 저장되는 데이터는 key: value쌍의 값으로 저장
또한, 인풋의 타입이 어떻게 되느냐에 따라 아웃풋의 타입도 유동적으로 변할 수 있도록 제네릭을 함께 설정해주어 Items라는 타입을 만들었음.
다음으로 추상화 클래스를 만들어 볼 차례
추상화를 시킬 땐 abstract라는 키워드를 사용
앞서 만들었던 Items라는 타입을 protected 키워드를 이용해 오직 하위 클래스에서만 접근 가능하도록 items라는 객체 필드에 타입을 지정해주었음
그리고 나머지 메소드들 또한 전부 abstract로 연결시켜주었으며, 제네릭을 활용함
마지막으로 실제 API로 사용될 SuperStorage라는 임의의 이름을 가진 새로운 클래스를 만들고 extends라는 키워드를 이용해 앞서 만든 추상화 클래스를 상속시켜 주었음
모범 답안에서는 모든 메소드에 대해 접근이 가능하도록 public 키워드를 사용
GeoLocation API
overloading을 활용하는 챌린지
overloading은 글자 그대로 이름은 동일하되 서로 다른 타입들을 덧붙이는 것이라고 이해하면 쉬움
먼저 GeolocationCoordinates의 타입을 설정
GeoLocation은 사용자의 로컬 컴퓨터의 위치를 좌표 형식으로 나타내줌. 사용법에 있는 메소드로 전달된 파라미터들 중에 각각optionsObj, errors, option 등은 전부 객체 형태인 것을 유추할 수 있음. 그렇기 때문에 각각 GeoOptions, GeoError, GeolocationCoords 그리고 Position이라는 타입을 각각 만들어서 필요한 필드들이 담긴 타입을 만들었음
다음으로 successFn, errorFn의 콜백 함수에 대한 타입을 설정할 차례
앞서 만든 GeoOptions, GeoError, GeolocationCoords 그리고 Position을 successFn과 errorFn의 파라미터에 적용시킬 타입으로 사용함
그렇게 SuccessFunction과 ErrorFunction이라는 타입을 만들었음.
이후, 사용법에 제시된 getCurrentPosition()과 watchPosition() 메소드의 전체 타입을 지정하도록 함. 앞서 만든 SuccessFunction 타입과 ErrorFunction을 연결 지을 수 있도록 GetCurrentPosition과 WatchCurrentPosition 타입을 만든 후, return되는 타입을 설정해주고, 이를 하나로 묶은 GeolocationAPI라는 interface를 만듦. 추후에 상속을 해야하니까.
마지막으로 Geolocator라는 클래스를 만듦. 이것이 실제로 API로 사용될 클래스이며 GeolocationAPI 타입을 연결. getCurrentPosition()과 watchPosition() 메소드에서 전달되는 error와 options는 없을 수도 있기 때문에 ? 연산자를 통해 필수가 아닌 선택적인 요소로 바꾸었음
// LocalStorage Interface
abstract class LocalStorage<T> {
protected items: Items<T>;
constructor() {
this.items = {};
}
abstract length(): number;
abstract key(index: number): T;
abstract getItem(key: string): T;
abstract setItem(key: string, value: T): void;
abstract removeItem(key: string): void;
abstract clear(): void;
}
interface Items<T> {
[key: string]: T;
}
class SuperStorage extends LocalStorage<string> {
constructor() {
super();
}
public key(index: number) {
return Object.keys(this.items)[index];
}
public length() {
return Object.keys(this.items).length;
}
public getItem(key: string) {
return this.items[key];
}
public setItem(key: string, value: string) {
this.items[key] = value;
}
public removeItem(key: string) {
delete this.items[key];
}
public clear() {
this.items = {};
}
}
// Geolocation Interface
type GeolocationCoords = {
latitude: number;
longitude: number;
altitude: number;
accuracy: number;
altitudeAccuracy: number;
heading: number;
speed: number;
};
type Position = {
coords: GeolocationCoords;
};
type GeoError = {
code: number;
message: string;
};
type SuccessFunction = (position: Position) => void;
type ErrorFunction = (error: GeoError) => void;
type GeoOptions = {
maximumAge: number;
timeout: number;
enableHighAccuracy: boolean;
};
type GetCurrentPosition = {
(success: SuccessFunction): void;
(success: SuccessFunction, error: ErrorFunction): void;
(success: SuccessFunction, error: ErrorFunction, options: GeoOptions): void;
};
type WatchCurrentPosition = {
(success: SuccessFunction): number;
(success: SuccessFunction, error: ErrorFunction): number;
(success: SuccessFunction, error: ErrorFunction, options: GeoOptions): number;
};
interface GeolocationAPI {
getCurrentPosition: GetCurrentPosition;
watchPosition: WatchCurrentPosition;
clearWatch: (id: number) => void;
}
class Geolocator implements GeolocationAPI {
getCurrentPosition: GetCurrentPosition = (
success: SuccessFunction,
error?: ErrorFunction,
options?: GeoOptions
) => {
return; // Implementation goes here :)
};
watchPosition: WatchCurrentPosition = (
success: SuccessFunction,
error?: ErrorFunction,
options?: GeoOptions
) => {
return 1; // Implementation goes here :)
};
clearWatch = (id: number) => {};
}
typescript설치
npm i -D typescript
package.json 초기화
npm init -y
tsconfig.json설정
디렉터리에 tsconfig.json 파일이 있으면 해당 디렉터리가 TypeScript 프로젝트의 루트임을 나타냅니다. tsconfig.json 파일은 프로젝트를 컴파일하는 데 필요한 루트 파일과 컴파일러 옵션을 지정합니다.
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#handbook-content
Target (기본값: ES3)
최신 브라우저는 모든 ES6 기능을 지원하므로 ES6는 좋은 선택입니다. 코드가 이전 환경에 배포된 경우 더 낮은 target을 설정하거나 최신 환경에서 코드 실행이 보장되는 경우 더 높은 target을 설정하도록 선택할 수 있습니다.
ex) 화살표 함수() => this는 ES5 이하이면 함수 표현식으로 바뀝니다.
특별한 ESNext 값은 TypeScript 버전이 지원하는 가장 높은 버전을 나타냅니다. 이 설정은 다른 TypeScript 버전 간에 동일한 의미가 아니며 업그레이드를 예측하기 어렵게 만들 수 있으므로 주의해서 사용해야 합니다.
https://www.typescriptlang.org/tsconfig#target
"build": "tsc" 또는 "npx tsc"
@ts-check
JavaScript 파일에서 오류를 활성화하려면 // @ts-check를 .js 파일의 첫 번째 줄에 추가하여 TypeScript가 오류를 발생시키도록 합니다. TypeScript는 여러 오류를 제공할 수 있습니다.
이러한 오류를 무시하고 싶다면 // @ts-ignore 또는 // @ts-expect-error를 추가하여 특정 줄의 오류를 무시할 수 있습니다.
https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check
JSDoc Reference
JSDoc 주석을 사용하여 JavaScript 파일에 type 정보를 제공할 수 있습니다. (자바스크립트 파일에서 타입 정보를 제공할 수 있습니다.)
https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
@param, @returns
/**
* @param {string} p1 - A string param.
* @param {string=} p2 - An optional param (Google Closure syntax)
* @param {string} [p3] - Another optional param (JSDoc syntax).
* @param {string} [p4="test"] - An optional param with a default value
* @returns {string} This is the result
*/
function stringsStringStrings(p1, p2, p3, p4) {
// 코드...
}
https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#param-and-returns
Inside of tsconfig.json what does the include property do?
It tells TS where to look for code to compile
Inside of tsconfig.json what does the outDir property do?
It tells TS where to put the output code
Inside of tsconfig.json what does the target property do?
It specifies the version of JS we want to compile to the to
Inside of tsconfig.json what does the lib property do?
It specifies what environment the code is goin to run on
What is a Type Definition file?
A TS file with comments that explains to TS the types of JS code
Can we use Typescript and Javascript in the same project?
Yes
What does //@ts-check do?
It tells TS to type check JS files
Can we use JSDoc on any JS file? Yes
TypeScript type 정의를 위한 리포지토리입니다.
https://github.com/DefinitelyTyped/DefinitelyTyped