Types of typescript - 1
//**Alias 타입 = ? -> 값을 넣어도 되고 안 넣어도 됨
//**Type 값 미리 지정 -> 앞에 type이 붙은 Player가 이것 -> 생성한 타입은 변수명 뒤에 ': + 타입명' 식으로 사용해줌
type Player = {
name : string,
age? : number
}
const nico : Player = {
name : "nico"
}
const lynn : Player = {
name : "lynn",
age : 12
}
//구지 그럴 필요는 없지만 type을 아래와 같이 사용 가능
type Age = number;
type Player2 ={
age? : Age;
}
//**함수의 return값 지정 (특히 return값으로 객체를 선택할 때의 형식 지정)
function playerMaker(name:string){
return{
name:name //혹은 name만
}//name을 담고 있는 객체를 return. -> 그냥 name만 return하면 문자열 return
}
const pla = playerMaker("nico");
//pla.age = 12; -> player자료형이 아니기에 오류 발생
function playerMaker2(name:string) : Player{ //함수가 return 하는 자료형
return{
name
}
}
const pla2 = playerMaker2("nico");
pla2.age = 12;
//**함수의 return값 지정(화살표)
const playerMaker3 = (name:string) => (name)
const playerMaker4 = (name:string) => ({name})
//그 외 타입 any(모든게 가능한 타입), null, undefined
Types of typescript - 2
//**readOnly1
type Age = number;
type Name = string;
type Player = {
readonly name:Name //readonly가 특이하게 쓰임
age?:Age
}
const playerMaker = (name:string) : Player => ({name})
const nico : Player = playerMaker("nico")
nico.age = 12
//nico.name = "las" -> readonly라 오류 발생
//**readOnly2
const num : number[] = [1,2,3,4]
const num2 : readonly number[] = [1,2,3,4]
num.push(4);
//num2.push(4); -> readonly때문에 오류 발생
//**Tuple
//항상 정해진 위치에 정해진 타입을 가져야 하는 array
const player : [string, number, boolean] = ["nico", 12, false]
const player2 : readonly [string, number, boolean] = ["nico", 12, false] //readonly 사용
Types of TypeScript - 3
//**Typescript 독특한 타입들
//1. unkown
//type 검사 후 쓸 수 있는 타입
let a:unknown;
// let b = a+1; -> unknown이라 불가능
if(typeof a === 'number'){ // unkown의 type 검사
let b = a+1;
}
//2. void
//일반적인 void와 같다
function hello(){ //void는 입력하지 않아도 설정됨
console.log('x')
}
//3. never
//절대로 return하지 않음.
function hello2():never{
throw new Error("xxx") ///오류를 발생시키는 문장
}
function hello3(name : string|number){
if(typeof name === "string"){ //type이 string
}else if(typeof name === "number"){ //type이 number
}else{ //여기 type이 never -> 정상적이면 절대 실행되면 안됨
}
}
call signiture
//**call signature
//함수 위에 마우스를 올렸을 때 나오는 메시지
//함수의 타입을 미리 만들어서 지정해 줄 수가 있다
type Add =(a:number, b:number) => number; //함수 타입 short cut
type Add2 = {
(a:number, b:number) : number;
} //이것도 함수 타입. 위와 같은 걸 길고 복잡하게 만들기
const add = (a:number, b:number) => (a+b)
const add2:Add = (a,b) => a+b; //a와 b, 그리고 return의 타입을 미리 지정해 줬기 때문에 오류 x
오버로딩
//**오버로딩
//하나의 함수에 여러개의 call signiture를 가짐
type Config = {
path : string,
state : object
}
type Push = {
(path:string):void
(config: Config):void //인자의 type 형태가 위와 다름
}
const push:Push = (config) => {
if(typeof config === "string")
console.log(config) //여기선 string type
else
console.log(config.path); //여기선 config type
}
//**오버로딩(타입의 개수가 다름)
type Add = {
(a:number, b:number) : number
(a:number, b:number, c:number) : number
}
const add:Add = (a, b, c?:number) => { //c는 선택할 수도 안 할수도 있기에 ?:를 달아줘야함
return a+b
//c가 있을 때도 작동하게 하려면 if(c)를 이용한 문장을 넣어줘야됨
}
다형성과 제너릭
//**다형성(polymorphos)
//poly = 많은, morphos = 구조
//배열을 받고 배열의 요소를 출력해주는 함수 작성(타입 상관 없이)
type SuperPrint = {
(arr: number[]):void
(arr: boolean[]):void
}
const superPrint:SuperPrint = (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([true, false])
superPrint([3, 4, 5])
//superPrint(["3", "4"]) -> string형이 선언에 없기에 오류
//superPrint([1, 2, true, false]) -> 이런 이상한 call signiture가 없기에 오류
//-> 위의 오류를 깔끔하게 해결하는 법(다형성, 제너릭 사용)
//**generic사용
//어떤 타입이 들어올지 확실히 모를 때
/*
'제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여
하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.'
*/
type SuperPrint2 ={
<Goguma>(arr: Goguma[]):void //Goguma가 제너릭 이름
//void대신 Goguma를 쓰면 return도 가능
}
type SuperPrint3 = <T>(arr: T[]) => void // 위와 같음
const superPrint2:SuperPrint2 = (arr) => {
arr.forEach(i=>console.log(i))
}
superPrint2([true, false])
superPrint2([3, 4, 5])
superPrint2(["3", "4"]) //제너릭은 call signature를 사용자가 요청하는 대로 생성함
const a = superPrint2([1, 2, true, false])
//a.toUpperCase() -> 에러발생
//그러면 any와 무슨 차이가 있냐고 할 수도 있지만 이런 상황에서 제너릭은 타입 보호를 해준다
//generic 2개 사용
type SuperPrint2 ={
<T, G>(arr: T[], arr2 : G): G
}
제너릭 결론
//내가 직접 제너릭을 만드는 경우는 많지 않고, 다른 패키지를 이용할 때, 제너릭을 사용하게 된다
//## 함수에서 제너릭 사용
function superPrint<T>(a: T[]){
return a[0]
}
const a = superPrint([1,2,3,4])
const b = superPrint<number>([1,2,3,4]) //원한다면 타입을 명시할 수도 있음
//## 타입문에서 제너릭 사용
/*
제너릭을 사용해 타입문을 만든다면(아래의 E와 F)
그 타입을 이용해서 변수를 만들 때, 제너릭이 무엇인지 명시해주어야 한다.
*/
type Player<E> = {
name : string
extraInfo : E
}
const nico : Player<{favFood : string}> = {
name : "nico",
extraInfo : {
favFood : "kimchi"
}
}
type ZYI<F> = {
(a:number, b:number) : F;
}
const zyi:ZYI<number> = (a, b) => a+b
//위의 코드와 형태는 다르고 내용은 같은 코드
type Player2<E> = {
name : string
extraInfo : E
}
type AdvancedPlayer2 = Player<{favFood : string}>
const nico2 : AdvancedPlayer2 = {
name : "nico",
extraInfo : {
favFood : "kimchi"
}
}
//기본적인 타입스크립트의 타입들은 제너릭으로 만들어져 있다
//## 배열 대신 Array 사용하기
type A = number[]
type B = Array<number> //Array형 역시 제너릭으로 만들어진 타입
let a1:A = [1,2,3,4]
let a2:B = [1,2,3,4]
클래스
//## 접근 권한 설정
class Player{
constructor(
private firstName:string,
private lastName:string,
public nickname:string
){} //생성자를 만들어주고 입력 매개변수 설정 + 접근 권한 설정
}
const zyi = new Player("nico", "las", "이주야");
//nico.firstName -> private라서 오류 발생
zyi.nickname
//##추상 클래스
//다른 클래스들이 상속받을 수 있는 클래스
//직접 인스턴스를 제작할 수는 없음
abstract class User{
constructor(
private firstName:string,
private lastName:string,
protected nickname:string
){} //생성자를 만들어주고 입력 매개변수 설정 + 접근 권한 설정
//이걸 private으로 만들면 더 이상 작동 x -> 메소드에서도 접근권한 설정 가능
getFullName(){
return `${this.firstName} ${this.lastName}`
}
//##추상 메소드
//상속받는 모든 것들이 구현 해야하는 메소드
//callsignature을 적어서 작성
abstract getNickName(arr:string):void //함수의 추상 메소드
}
class Player2 extends User {
getNickName(){
console.log(this.nickname)
/*private이라면 직접 접근은 x, 그런데 protected로 바꿔줘서
상속받은 자식 클래스에서도 접근 가능*/
}
}
//const zyi = new User("1", "2", "3") -> 추상 클래스로 직접 만들려 해서 오류
const zyi2 = new Player2("nico", "las", "이주야");
zyi2.getFullName()
클래스2
type Words = {
[word : string] : string //key의 타입, value의 타입 명시
}
//## 간단한 단어 프로그램 만들기
class Dict{
// constructor(
// private words : Words
// ){} 기본적인 word라는 변수의 선언 방법
private words : Words
constructor(){
this.words = {
"갤" : "갤갤갤"
} //수동으로 초기화
}
//아래는 function이 아니라 method임
add(word : Word){ //클래스를 타입으로 사용
if(this.words[word.term] === undefined){
this.words[word.term] = word.def;
}
}
def(term:string){
return this.words
}
}
class Word{
constructor(
public term : string,
public def : string
){}
}
const kimchi = new Word("kimchi", "한국의 음식");
const sushi = new Word("sushi", "일본의 음식");
const dict = new Dict()
dict.add(kimchi);
dict.add(sushi);
console.log(dict.def("kimchi"));
interface
//## 특정 값만 사용 가능
type Team = "red" | "blue" | "yellow"
type Health = 1 | 5 | 10
const team : Team = "red";
//const health : Health = 2; -> 1/5/10 중의 값이 아니면 에러
//## interface
type Player1 = {
nickname : string
}
interface Player2 { //interface는 객체의 타입만을 설명할 수 있는 키워드
nickname : string
}
interface 상속
//## interface 상속
interface User1 {
name : string
}
interface Player1 extends User1{
}
const nico1 : Player1 = {
name : "nico"
}
//위와 같은걸 type으로 쓴다면
type User2 {
name : string
}
type Player2 = User2 & {
}
const nico2 : Player2 = {
name : "nico"
}
interface 합치기
//## interface 합치기
interface User{
name : string
}
interface User{ //같은 이름을 가진 interface를 쓰면 하나로 합쳐짐
sex : string
}
interface User{
age : number
}
const jiwon : User = {
name : "jiwon",
sex : "male",
age : 24
}
interface 장점
//아래의 User를 추상클래스로 구현하게 되면 js코드에 User에 대한
//일반 클래스가 자동으로 생성된다
//하지만 interface로 User를 만들고 extends대신 implements를 쓰면
//User에 대한 클래스가 생성되지 않는다.
interface User{
firstNmae: string
sayHi(name:string):string
}
class Player implements User{
constructor(
public firstNmae : string
){}
sayHi(){
return "hoho";
}
}
//##interface를 타입처럼 사용 가능
const user : User = {
firstNmae : "!",
sayHi(name){
return "1"
}
};