let 이름: string = 'kim';
let 회원들: (string | number)[] = ['kim', 'park', 1];
let text = ['kim', 'park', 1];
let 회원들2: {
member: string;
member2: string;
} = {
member: 'kim',
member2: 'park',
};
// TS는 대부분 타입지정을 자동으로 해주니 위 코드들처럼
//너무 덕지덕지 붙일 필요 없다.(ex; text에 커서 올려봐라)
let myName: string = 'cho';
let myAge: number = 31;
let 출생지역: string = '김해';
let myFavorite: { title: string; singer: string } = {
title: '막걸리',
singer: '장범준',
};
let project: {
member: string[];
days: number;
started: boolean;
} = {
member: ['kim', 'park'],
days: 30,
started: true,
};
let 회원들: (number | string)[] = [1, '2'];
let 오브젝트: { a: string | number } = { a: '123' };
// any : TS 보호막 해제 사용을 지양하라
let 이름: any = 1;
이름 = 'string';
let 변수: number = 이름; //TS 아예 무시 에러발생 X
//unknown : any랑 비슷 but
let 이름2: unknown;
이름2 = 1;
이름2 = {};
// 지금 이름2의 타입은 객체이기 때문에 string 변수에
//할당 불가하다는 에러가 뜸.(any는 노상관)
let 변수1: string = 이름2;
이름2 - 1; //unknown타입은 수학연산 불가
// 수학 연산 가능 : any number bigint
/*
JS는 문자에도 + 사용가능. 타입스크립도 가능하지만
엄격히 number나 string 타입에만 가능하고
string|number 는 별도의 타입이기 때문에 허용하지 않는다.
*/
let 나이: string | number;
나이 + 1;
// -----
let age: unknown = 1;
나이 - 1; // unknown은 연산 허용하지 않는 타입이니까
//Narrowing / Assertion으로 엄격히 코드 짜면 됨.
왜 에러가 날까?
Q) 1
let 나이: string | number;
나이 + 1;
Q) 2
let 나이: unknown = 1;
나이 + 1;
A) TS는 엄격함
string | number 는 union타입으로 새로운 타입을 생성한 것
unknown은 당연히 연산 허용 X 타입이라서 에러 발생.
Q) 2
let user: string = 'kim';
let age: undefined | number = undefined;
let married: boolean = false;
let 철수: (string | undefined | number | boolean)[] = [user, age, married];
Q) 3
let 학교: {
score: (number | boolean)[];
teacher: string;
friend: string | string[];
} = {
score: [100, 97, 84],
teacher: 'Phil',
friend: 'John',
};
학교.score[4] = false;
학교.friend = ['Lee', 학교.teacher];
function 함수(x: number): number {
return x * 2;
}
//타입지정된 파라미터는 필수로 넣어야함
함수(); //에러
// void
function 리턴안해(x: number): void {
console.log('리턴ㄴㄴ');
}
// 옵셔널 넣어주면 인자 없어도 에러 띄우지 않음.
function 옵션파라미터(x?: number) {}
옵션파라미터();
// x?: number === x: number | undefined
// 중간 퀴즈
function 퀴즈(x: number | string): void {
console.log(x + 3); // 1. 에러가 뜨는 이유?
// 유니온 타입은 새로운 타입이기 때문.
// string, number, any 타입이 아니란 것.
}
퀴즈(2);
// 2. 어떻게 에러를 해결해볼 수 있을까
// Narrowing!!
function 에러해결(x: number | string): void {
if (typeof x === 'number') {
console.log(x + 3);
}
}
// 숙제1 이름 넣으면 안녕하세요 이름, 안넣으면 이름 없다
//출력하는 함수 만들기
function 인사(x?: string): void {
if (typeof x === 'string') {
// if(x) 도 가능
console.log(`안녕하세요 ${x}`);
} else if (typeof x === 'undefined') {
// else만 해도 됨.
console.log('이름이 없습니다.');
}
}
//숙제2 함수에 숫자 또는 문자를 넣으면 자릿수를 세어 출력하는 함수
function 자리수세기(x: number | string): void {
let count: number = 0;
if (typeof x === 'number') {
let st = x.toString();
for (let i = 0; i < st.length; i++) {
count++;
}
} else {
for (let i = 0; i < x.length; i++) {
count++;
}
}
console.log(count);
}
바보다.걍 문자열.length 깜빡함..
// console.log(x.toString().length)
// 숙제3 결혼 가능 확률 계산기
/*
1. 파라미터, 월소득(만원), 집보유(boolean), 매력(상중하)
2. 월소득 1점, 집 500점, 매력점수 상 100점
3. 총 점수 >= 600 결혼가능 리턴, 그 외엔 아무것도 리턴X
*/
function 결혼가능판독기(월소득:number,집보유:boolean,매력:string):string|undefined {
let score = 0;
score += 월소득;
if (집보유) score += 500;
if (매력 === '상') score += 100;
if(score>=600) return '결혼가능'
}
function 내함수(x: number | string) {
if (typeof x === 'number') {
return x + 1;
} else {
return x + '1';
}
}
console.log(내함수('1'));
function 함수(x: number | string) {
let array: number[] = [];
array[0] = x; //에러
// x가 확실히 number일 때만 들어갈 수 있음.
if (typeof x === 'number') {
array[0] = x;
}
}
// Narrowing 판정 문법
/*
1. typeof 변수
2. 속성명 in obj
3. 인스턴스 instancof 부모
*/
function 내함수(x: number | string) {
let array: number[] = [];
array[0] = x as number;
(x as string) + 1; //현재문법
<string>x + 1; //옛날문법
// 편하지만 용도가 있음.
// x as boolean <- 이렇게 타입 변경해주는 게 아니고
// union 타입 등으로 인한 불확실성을 확실하게 해주는 용도
}
- 용도1. Narrowing할 때
- 용도2. 무슨 타입이 들어올지 100% 확실할 때 사용
- reason: 이것도 any랑 비슷하게 타입에러를 잡아주지 않음.
type Person = {
name: string;
};
function 변환기<T>(data: string): T {
return JSON.parse(data) as T;
}
const jake = 변환기<Person>('{"name":"kim"}');
console.log(jake); // { name: 'kim' }
//연습1: 배열에 문자타입 숫자를 number로 변환기
function numberConverter(x: (number | string)[]): number[] {
return x.map((num) => {
return Number(num);
});
}
//연습2 1과목 쌤 스트링 2과목이상 배열
/*
let 철수쌤 = { subject : 'math' }
let 영희쌤 = { subject : ['science', 'english'] }
let 민수쌤 = { subject : ['science', 'art', 'korean'] }
만들함수( { subject : 'math' } ) //이 경우 'math'를 return
만들함수( { subject : ['science', 'art', 'korean'] } ) //이 경우 'korean'을 return
만들함수( { hello : 'hi' } ) //이 경우 타입에러 나면 됩니다
*/
function 과목반환(x: { subject: string | string[] }): string {
if (typeof x.subject === 'string') {
return x.subject;
} else if (Array.isArray(x.subject)) {
return x.subject[x.subject.length - 1];
} else {
return '과목이 없습니다.';
}
}
// 원시타입
type Animal = string | number | undefined;
let 동물: Animal = 123;
동물 = 'answk';
동물 = undefined;
// 객체
type CamelType = { name: string; age: number };
let 타조: CamelType = { name: 'kim', age: 20 };
// readonly
type CountryHome = {
readonly region: string;
};
const 출생지역: CountryHome = { region: 'Seoul' };
출생지역.region = 'Busan'; //에러
// 타입 alias도 union type화 가능.
type Name = string;
type Age = number;
type Person = Name | Age;
// --- 객체 타입 Extend(합침)
type X = { x: number };
type X = { z: string };
//타입 재정의 불가!
type Y = { y: number };
type NewType = X & Y;
const 합치기: NewType = { x: 1, y: 2 };
let 이름: 'kim';
이름 = 'park'; //에러
// 다수도 가능
let 나야: '대머리' | '솔로';
나야 = '커플'; //에러
function 함수(a: 'hello'): 1 {
return 1;
}
함수('bye'); //에러
//중간 연습
//1. 가위바위보 파라미터
//2. 가위 바위 보만 들어갈 수 있는 array 리턴
function 가위바위보(param: '가위' | '바위' | '보'): ('가위' | '바위' | '보')[] {
return ['가위'];
}
결국 Literal type은 const 키워드의 업글버전
let 성별: '남자' | '여자';
성별 = '남자';
성별 = '여자';
성별 = '자웅동체'; //에러
let 자료 = {
name: 'kim',
};
자료.name; // 'kim'
// Literal type의 문제점
function 내함수(a: 'kim') {}
내함수('kim');
내함수(자료.name); //에러
- 설명
a: 'kim'은 'kim'이라는 타입을 지정한 것
a가 'kim'이 아니라.
자료.name의 타입은 string
a:'kim'은 타입 'kim'
// 첫번째 방법 : 첨부터 타입 지정
let 자료2: { name: 'kim' } = { name: 'kim' };
// name 값의 타입은 string이 아닌 'kim' 임
내함수(자료2.name);
// 두번째 방법 : assertion 타입을 덮어버림.
내함수(자료.name as 'kim');
let 자료3 = {
name: 'kim',
} as const; //(자물쇠!)
// 프로퍼티의 값을 키의 type으로 선언한다~
// 또한 readonly 속성도 부여함.
//## 함수와 methods에 type alias 지정하기
//- 함수타입 alias는 함수선언문 x 화살표함수로
type 함수타입 = (a: string) => number;
//사용은 함수 표현식에
const 함수: 함수타입 = function (a) {
return 10;
};
console.log(함수('a')); //10
//## 메서드 type alias
type Member = {
name: string;
age: number;
plusOne: (x: number) => number;
changeName: () => void;
};
let 회원정보: Member = {
name: 'john',
age: 23,
plusOne(x) {
return this.age + x;
},
changeName: () => {
console.log(this.name); //error
},
};
console.log(회원정보.plusOne(1)); //24
회원정보.changeName(); //undefined
type Zero = (x: string) => string;
const cutZero: Zero = (x) => {
let result: string = '';
if (x[0] === '0') {
result = [...x].splice(1, x.length - 1).join('');
} else {
result = x;
}
return result;
};
// 계속 [...x] 에서 에러가 났는데
// confing가 es5라 최신문법 적용이 안됐음.
// 하지만 호환성때문에 es5쓰는게 낫다는데..
다른 풀이 (정규표현식 공부 정리해보자.)
const cutZero2: Zero = (x) => {
let result = x.replace(/^0+/, '');
return result;
};
// 연습 2) 스트링의 '-' 모두 빼버리고 숫자로 리턴
function removeDash(x: string): number {
let result = x.replace(/-/g, '');
return Number(result);
}
//연습 3) 콜백함수 연습2함수 결과를 연습1함수에 넣고
//결과를 콘솔창에 띄우기
type 함수1 = (a: string) => string;
type 함수2 = (a: string) => number;
function 종합함수(x: string, y: 함수1, z: 함수2): void {
let a = z(x);
let result = y(a);
console.log(result);
}
//첫번째 실습
let 제목 = document.querySelector('#title');
// ### narrowing 방법 5가지
//힌트) 제목에 커서 올려보면 Element | null
//null인 경우 제외
if (제목 !== null) {
제목.innerHTML = '반가워요';
}
// 가장 권장하는 방법
if (제목 instanceof Element) {
제목.innerHTML = '반가워요';
}
// optional 연산자
if (제목?.innerHTML) {
제목.innerHTML = '반가워요';
}
//assertion
let 제목2 = document.querySelector('#title') as Element;
// 5번째 tsconfig의 nullcheck 옵션 false
//두번째 실습
let 링크 = document.querySelector('.link');
// element의 정확한 타입명을 지정.
if (링크 instanceof HTMLAnchorElement) {
링크.href = 'https://kakao.com';
}
/*
HTMLAnchorElement
HTMLHeadingElement
HTMLButtonElement
...
*/
//세번째 실습
let 버튼 = document.querySelector('#button');
// 얘는 아래처럼 희안하게 옵셔널만으로 퉁쳐짐.
버튼?.addEventListener('click', function () {});
// ### 연습해보기1
// src의 src를 new.jpg로 바꾸기
let 이미지 = document.querySelector('#image');
if (이미지 instanceof HTMLImageElement) {
이미지.src = 'new.jpg';
console.log(이미지.src);
}
// ### 연습해보기2
// 여러개의 html요소 전부 바꾸기.
let 앵커들 = document.querySelectorAll('.naver');
console.log(앵커들);
앵커들.forEach((element) => {
if (element instanceof HTMLAnchorElement) {
element.href = 'kakao.com';
}
});
//## class, prototype 벨로그 링크.
// ## class에 타입지정하기
class Person {
name;
age;
constructor(a = '길동', b = 20) {
this.name = a;
this.age = b;
}
함수(a: string): void {
console.log(`hi ${this.name}${a}`);
}
}
let 홍길동 = new Person('길동', 10);
console.log(홍길동);
홍길동.함수('님');
// ### 연습해보기
// 위 Person class의 컨스트럭터의 파라미터 타입
// 지정 안해도 되는 방법이 있다는데?
// a,b가 undefined면 기본값 설정해둔 걸로 들어감.
constructor(a = '길동', b = 20){
this.name = a;
this.age = b
}
class Person {
add(숫자: number): void {
console.log(숫자 + 1);
}
}
//### 연습
class Car {
model;
price;
constructor(a: string, b: number) {
this.model = a;
this.price = b;
}
tax(): void {
console.log(this.price * 0.1);
}
}
let car1 = new Car('소나타', 3000);
console.log(car1);
//Car { model: '소나타', price: 3000 }
car1.tax();
//300
//### 연습2
class Word {
nums;
strings;
constructor(a: (number | string)[]) {
this.nums = [];
this.strings = [];
for (let i = 0; i < a.length; i++) {
if (typeof a[i] === 'string') {
this.strings.push(a[i]);
} else {
this.nums.push(a[i]);
}
}
}
}
let arr = new Word(['a', 1, 'kim', 123123]);
console.log(arr);
//Word { nums: [ 1, 123123 ], strings: [ 'a', 'kim' ] }
// 모범 답안.
class Word2 {
num;
str;
constructor(...param: (number | string)[]) {
let 숫자들: number[] = [];
let 문자들: string[] = [];
param.forEach((i) => {
if (typeof i === 'string') {
문자들.push(i);
} else {
숫자들.push(i);
}
});
this.num = 숫자들;
this.str = 문자들;
}
}
type Square = {
color: string;
width: number;
};
//위랑 같음
interface Square2 {
color: string;
width: number;
}
let 네모: Square2 = { color: 'red', width: 100 };
- 중간퀴즈
interface Student {
name: string;
}
// interface Teacher {
// name: string;
// age: number;
// }
// extends이용하여 인터페이스 하나로 쓰기
interface Teacher extends Student {
age: number;
}
let 학생: Student = { name: 'kim' };
let 선생: Teacher = { name: 'kim', age: 20 };
// type alias에서 합쳤던거 기억하기
//intersection type
// & 좌 우 다 만족하는 객체 타입 지정해주세요~
type dog = { name: string };
type cat = { age: number } & dog;
const animal: cat = {
age: 123,
name: 'gg',
};
// 1. interface는 중복선언 가능
interface A {
name: string;
}
interface A {
age: number;
}
//A인터페이스는 name과 age 모두 가짐.
// 2. 장점 외부 라이브러리의 interface를
// 내가 확장(커스터마이징)해서 사용 가능.
// 다른 사람들이 내가만든 obj 많이 쓸 것 같으면
// interface 권장.
근데 interface의 속성이 중복되면?
interface 중복 {
name: string;
}
interface 중복대상 extends 중복 {
name: number;
}
//아래 상황이나 똑같은 상황이라 허용하지 않음.
interface 중복대상 {
name: string;
name: number;
}
& 쓸 떄 중복이 발생하면 ?
type Animal = { name: string };
type Tajo = { name: number } & Animal;
// 합칠 때는 에러가 발생하지 않는다.
let a: Tajo = { name: 'ss' }; // type never
// 하지만 Tajo(number)도 만족하고
// Animal(string)도 만족하라는 명령이라
// string이나 number를 값으로 할당해주면
//에러가 발생한다.
연습해보기
interface Product {
brand: string;
serialNumber: number;
model: string[];
}
let 상품: Product = {
brand: 'Samsung',
serialNumber: 1360,
model: ['TV', 'phone'],
};
interface Bucket {
product: string;
price: number;
}
let 장바구니: Bucket[] = [
{ product: '청소기', price: 7000 },
{ product: '삼다수', price: 800 },
];
interface Bucket {
product: string;
price: number;
}
let 장바구니: Bucket[] = [
{ product: '청소기', price: 7000 },
{ product: '삼다수', price: 800 },
];
interface Bucket2 extends Bucket {
card: boolean;
}
{ product : '청소기', price : 7000, card : false }
interface Calc {}
let obj = {
plus(a, b) {
return a + b;
},
minus(a, b) {
return a - b;
},
};
interface Calc {
plus: (a: number, b: number) => number;
minus: (a: number, b: number) => number;
}
let obj: Calc = {
plus(a, b) {
return a + b;
},
minus(a, b) {
return a - b;
},
};