요즘 대세를 따르기 위해 타입스크립트 기초 강좌를 보며 정리해보려한다. 그동안 JavaScript만 써왔어서 걱정되지만 변화하는 흐름을 따라가기 위해.... 영상은 코딩앙마 유튜브 영상을 시청했다.
자바스크립트에서 실행
function add(num1,num2){
console.log(num1+num2);
}
add(); // NaN
add(1) // NaN
add(1,2) // 3
add(3,4,5) // 7
add('hello','world');//'helloworld'
숫자 두개를 받아서 둘을 더하는 함수를 만든다고 가정해보자. 인간은 글자만 봐도 인수가 number 여야 한다는 것을 알지만 컴퓨터는 그것을 모른다. 그러나 add함수에 인자 없이 함수를 실행할 때 자바스크립트에서 실행할 경우 아무런 오류 없이 NaN 이 반환된다. (undefined+undefined = NaN), 그 외 인수의 개수를 맞추지 않거나 의도한 바가 아닌 문자열을 입력해도 자바스크립트는 아무런 경고 없이 문자열도 더해준다ㅠ
자바스크립트에서 실행
function showItems(arr){
arr.forEach((item)=>{
console.log(item);
});
}
showItems([1,2,3]) ; // 1 2 3
showItems(1,2,3) // TypeError (배열이 아니라 에러 뜸)
배열의 요소를 반환하는 showItems 함수에 배열이 아닌 값을 넣게 되면 타입 에러가 뜬다.
JavaScript는 동적언어, 런타입에 타입이 결정되고 오류를 발견한다. 개발자가 실수할 때 사용자는 고스란히 오류를 발견하게 된다.
반면 Java 나 TypeScript의 경우는 정적언어, 컴파일할 때 타입이 결정되고 오류를 발견한다.
function add(num1:number,num2:number){
console.log(num1+num2);
}
add(); // error : Expected 2 arguments, but got 0
add(1) // error : Expected 2 arguments, but got 1
add(1,2) // 3 (^^)
add(3,4,5) // error : Expected 2 arguments, but got 3
add('hello','world');// error : Argument of type 'string' is not assignable to parameter of type 'number'
function showItems(arr:number[]){
arr.forEach((item)=>{
console.log(item);
});
}
showItems([1,2,3]) ; // 1 2 3
showItems(1,2,3) // error : Expected 1 arguments, but got 3
이처럼 타입스크립트는 개발하면서 발생할 수 있는 타입 에러들을 미리 감지하여 알려준다!
//string
let car: string = 'bmw';
// car = 3 ; // type error
//number
let age: number = 30;
//boolean
let isAdult: boolean = true;
//array (number)
let a: number[] = [1, 2, 3];
let a2: Array<number> = [1, 2, 3];
//array(string)
let week1: string[] = ['mon', 'tue', 'wed'];
let week2: Array<string> = ['mon', 'tue', 'wed'];
// 튜플(Tuple) - 인덱스별로 타입 다를 때 사용 가능
let b: [string, number] = ['hello', 123];
b[0].toLowerCase(); // 가능
// b[1].toLowerCase() // error : 'toLowerCase' does no exist on ype 'number'
//void - 아무것도 반환하지 않을 때
function sayHello(): void {
console.log('hello');
}
//never - 항상 에러 반환하거나 영원히 끝나지 않는 타입
function isfLoop() {
while (true) {
//do something..
}
}
//enum - 비슷한 값들끼리 묶어줌 , 알아서 1씩 증가시켜서 값 배정
enum Os {
Window, //1
Ios, //2
Android, //3
}
let myOs: Os; // Os에 있는 것만 입력할 수 있음
// null , undefined
let nulla: null = null;
let nullb: undefined = undefined;
인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다. (속성과 속성의 타입, 파라미터, 접근 방식, 클래스 등)
user라는 object 를 만든다고 가정해보자.
let user:object;
user = {
name : 'xx',
age : 30
}
console.log(user.name) // error : Property 'name' does not exist on type 'object'
프로퍼티 정의해서 객체로 표현하고자 할 때는 인터페이스를 사용한다.
type Score = 'A' | 'B' | 'C' | 'F';
interface User {
name: string;
age: number;
gender?: string; // optional 한 항목 만드려면 항목 이름 뒤에 ? 붙이고 타입 설정해준다.
readonly birthYear: number; // 값 수정 못하게 하려면 항목 이름 앞에 readonly 라고 써준다.
[grade: number]: Score; // 문자열 인덱스 서명 추가하기 [key : type] : type (이 경우는 직접 Scroe 타입 만들어서 Score타입 넣어줌)
}
let user: User = {
name: 'xx',
age: 30,
birthYear: 2000,
1: 'A',
2: 'B',
};
user.gender = 'male';
// user.birthYear = 1900;//error:Cannot assign to 'birtyYear' because it is a read-only property
console.log(user.age); // 30
interface Add {
(num1:number,num2:number) : number;
}
const add : Add = function (x,y){
return x + y;
}
add(10,20) // 30
interface IsAult {
(age:number):boolean;
}
const a:IsAdult = (age) =>{
return age > 19;
}
a(33) // true
//implements
interface Car {
color : string;
wheels : number;
start() :void;
}
class Bmw implements Car{
color;
wheels = 4;
constructor(c:string){
this.color = c'
}
start(){
console.log('go..');
}
const b = new Bmw('green')
consoe.log(b) // 'wheels' : 4, 'color' : 'green'
b.start(); // 'go...'
interface Benz extends Car{
door : number;
stop() : void;
}
const benz : Benz = {
door : 5,
stop(){
console.log('stop')
}
color : 'black',
wheels : 4,
start
}
interface Car {
color : string;
wheels : number;
start():void;
}
interface Toy {
name : string;
}
interface ToyCar extends Car, Toy {
price : number;
}
function add(num1:number,num2:number):number{
return num1+num2;
}
//반환값 없는 경우에는 함수의 반환값 타입을 void로 지정
function add(num1:number,num2:number):void{
console.log(num1+num2)
}
function hello(name?:string){ // name 뒤에 ? 붙여서 optional로 지정 (입력 해도 되고 안해도 됨)
return `Hello, ${name||"world"}`;
}
function hello2(name = 'world'){
return `Hello, ${name}`;
}
const result = hello()
const result2 = hello('sam')
const result3 - hello(123) // error
function hello(name:string, age?:number):string{ // 선택적 매개변수는 무조건 필수 매개변수 뒤에!
if(age !== undefined){
return `Hello, ${name}. You are ${age}.`'
}else {
return `Hello, ${name}`;
}
}
console.log(hello('Sam')); // Hello, Sam
console.log(hello('Sam',30));//Hello, Sam. You are 30
function add(...nums: number[]){
return nums.reduce((result,num)=> result + num,0);
}
add(1,2,3) // 6
add(1,2,3,4,5,6,7,8,9,10) //55
this의 타입은 맨 앞에 써준다.
interface User{
name:string;
}
const Sam : User = {name : 'Sam'}
function showName(this:User , age:number,gender:'m'|'f'){
console.log(this.name,age,gender)
}
const a = showName.bind(sam);
a(30,'m'); // "Sam , 30, 'm'"
함수 오버로드는 전달받은 매개변수의 개수나 타입에 따라 다른 동작을 하게하는 것을 말한다.
interface User3 {
name: string;
age: number;
}
//함수 오버로드
function join(name: string, age: string): string; // age가 string이면 string 반환
function join(name: string, age: number): User3; // age가 number면 User반환
function join(name: string, age: number | string): User3 | string {
if (typeof age === 'number') {
return {
name,
age,
};
} else {
return '나이는 숫자로 입력하셈';
}
}
const sam: User3 = join('Sam', 30); // 'Sam',30'
const jane: string = join('Jane', '30'); // '나이는 숫자로 입력하셈'