리터럴 타입은 정해진 값을 가진 타입이다.
const userName1 = 'Bob'; // 얘는 재할당 불가능하므로 무조건 'Bob'임
let userName2 : string | number = 'Tom';
userName2 = 3 // ㄴ> 이렇게 string이랑 number 둘 다로 설정 안해놓으면 초기 타입이 string이었기 때문에 string만 넣을 수 있음
type Job = 'police' | 'developer' | 'teacher'
interface User {
name : string;
job : Job;
}
const user : User = {
name : 'Bob',
// job : 'student', < Job 타입에 지정된 값이 아니므로 에러뜸
job : 'developer',
}
interface HighSchoolStudent {
name : string,
grade : 1 | 2 | 3
}
| (or 조건 타입) , 동일한 속성의 타입을 다르게 해서 구분할 수 있는 것을 식별 가능한 유니온 타입이라고 한다.
interface Car {
name : 'car';
color : string;
start():void;
}
interface Mobile{
name : "mobile"
color : string;
call():void;
}
function getGift(gift : Car | Mobile){
console.log(gift.color);
//gift.start(); error : Property 'start' does not exist on type 'Mobile' start() 는 Car에만 있어서
if(gift.name==='car'){
gift.start();
}else{
gift.call();
}
}
& (and 조건 타입) , 모든 속성을 다 기입하기 전까지는 에러 안사라진다. 교차타입은 여러개의 타입을 하나로 합쳐준다. 필요한 모든 기능을 가진 하나의 타입을 만들어주는 것이다.
interface Car {
name : string;
start() : void;
}
interface Toy {
name : string;
color : string;
price : number;
}
const toyCar : Toy & Car = {
name : '타요',
start(){}.
color : 'red',
price : 1000,
}
타입스크립트에서는 자바스크립트 es6의 클래스와 달리 멤버 변수를 constructor 전에 먼저 선언해줘야한다.
class Car {
color: string
constructor(color:string){
this.color = color;
}
start(){
console.log('start')
}
}
const bmw = new Car('red')
먼저 선언하지 않는 방법은 readonly 사용하면됨
es6 의 클래스는 접근제한자 제공하지 않음, 하지만 타입스크립트는 접근 제한자 제공한다. 타입스크립트의 접근제한자는 public, private,protected가 있다.
아무것도 안쓰면 public 이다
class Car {
name : string = 'car';
color : string;
constructor(readonly color:string){
this.color = color;
}
start(){
console.log('start')
}
}
class Bmw extends Car {
constructor(color:string){
super(color);
}
showName(){
console.log(super.name);
}
}
const z4 = new Bmw('black')
속성 이름 앞에 private
를 쓰거나 #
을 입력하면 private 속성으로 해당 클래스에서밖에 사용하지 못한다.
class Car {
private name : string = 'car';
//#name : string = 'car';
color : string;
constructor(readonly color:string){
this.color = color;
}
start(){
console.log('start')
}
}
//위 코드처럼 Bmw 에서는 못쓴다
protected도 public과 마찬가지로 자식클래스에서 접근이 가능하다. public과 다른 점은 자식클래스 내부에서는 참조할수있으나 클래스 인스턴스로는 참조할 수 없다.
class Car {
protected name : string = 'car';
color : string;
constructor(readonly color:string){
this.color = color;
}
start(){
console.log('start')
}
}
class Bmw extends Car {
constructor(color:string){
super(color);
}
showName(){
console.log(super.name);
}
}
const z4 = new Bmw('black')
console.log(z4.name) // error : property 'name' is protected and only accessible within class 'Car' and its subclassess
- public : 자식클래스, 클래스 인스턴스에서 모두 접근 가능
- protected : 자식클래스에서 접근 가능
- private : 해당 클래스 내부에서만 접근 가능
static 이용하면 정적 멤버 변수 만들 수 있다. 접근하려면 클래스이름.속성 으로 접근해야한다.
class Car {
protected name : string = 'car';
color : string;
static wheels = 4;
constructor(readonly color:string){
this.color = color;
}
start(){
console.log('start')
console.log(Car.wheels); //이렇게 클래스명을 써줘야한다!
}
}
추상클래스는 new
를 이용하여 객체 만들 수 없고 상속을 이용해서만 사용가능함. class 앞에 abstract
기입
abstract class Car {
protected name : string = 'car';
color : string;
static wheels = 4;
constructor(readonly color:string){
this.color = color;
}
start(){
console.log('start')
}
abstract doSomething(): void // 추상메서드는 반드시 상속 받은 곳에서 구체적인 구현을 해줘야함, 여기서는 이름 정도만 정한다고 생각하면 된다.
}
const car = new Car('red') // error
class Bmw extends Car{
. . .
doSomething(){
alert(3)
}//이렇게 상속받은 곳에서 어떤 기능을 할건지 구현을 해줘야한다.
} // 이렇게만 가능
제네릭을 이용하면 클래스나 함수, 인터페이스를 다양한 타입으로 재사용 할 수 있다. 선언할 때는 그냥 파라미터만 적어주고 사용할 때 파라미터의 구체적인 타입을 정해준다.
이렇게 일일이 타입을 다 적어주면 번거롭기도 하고 정해진 타입이 아닌 경우 재사용성이 떨어진다.
function getSize(arr : number[] | string[] | boolean[] | object[] ):number{
return arr.length
}
const arr1 = [1,2,3]
getSize(arr1) // 3
const arr2 = ['a','b','c']
getSize(arr2) // 3
const arr3 = [true,false,true]
getSize(arr3) // 3
const arr4 = [{},{},{name:'Tims'}]
getSize(arr4) // 3
이 때 제네릭을 이용하면 편하게 재사용성을 높일 수 있다.
function getSize<T>(arr:T[]):number{
return arr.length
}
//<T> (꼭 T가 아니어도 됨, 그냥 타입이라는 뜻인듯) 를 써주고 파라미터의 타입을 배열이라는 것만 설정한다. 그 후 아래처럼 사용할 때 원하는 타입 쓰면 된다.
const arr1 = [1,2,3]
getSize<number>(arr1) // 3
const arr2 = ['a','b','c']
getSize<string>(arr2) // 3
//사실 꼭 <string> 이렇게 안써줘도 받는 값의 타입을 알아서 인식해서 타입 지정해준다.
const arr3 = [true,false,true]
getSize<boolean>(arr3) // 3
const arr4 = [{},{},{name:'Tims'}]
getSize<object>(arr4) // 3
제네릭을 이용하면 하나의 인터페이스를 선언하고 각기 다른 모습의 객체들을 만들어줄 수 있다.
interface Moblie<T>{
name : string;
price : number;
option:T // 뭐가 들어올지 몰라서 제네릭으로 타입 지정 안하고 넘어감
}
const m1 : Mobile<object> = {
name : 's21',
price : 1000,
option : {
color : 'red',
coupon: false,
},
}
const m2 : Mobile<string> = {
name : 's20',
price : 1300,
option : 'good'
}
//만약 option 의 형태가 정해져있다면 이렇게 미리 쓰는 것도 된다
const m1 : Mobile<{color:string;coupon:boolean}> = {
name : 's21',
price : 1000,
option : {
color : 'red',
coupon: false,
},
}
interface User{
name:string;
age : number;
}
interface Car {
name : boolean;
color:string;
}
interface Book {
price: number;
}
const user:User = {name : 'a',age:10}
const car:Car = {name : 'bmw',color:'red'}
const book : Book = {price:3000}
//어떤 T타입이 올건데 그 타입은 name이 string인 객체를 확장한 것이다
function showName<T extends {name : string}>(data:T):string{
return data.name
}
showName(user)
showName(car) // error : name이 string이 아님
showName(book) // error : book 에는 name 없음
keyof 키워드를 이용하면 key값들을 유니온 형태로 받을 수 있다.
interface User{
id : number;
name : string;
age : number;
gender : 'm'|'f'
}
type UserKey = keyof User; //'id' | 'name' | 'age' | 'gender'
const uk : UserKey = 'id'
Partial<T>
프로퍼티를 모두 optional로 바꿔준다.
interface User{
id : number;
name : string;
age : number;
gender : 'm'|'f'
}
//이렇게 인터페이스를 Partial로 묶어주면 User의 프로퍼티가 모두 optional이 된다
let admin : Partial<User> = {
id:1,
name:'Bob'
}
interface User{
id? : number;
name? : string;
age? : number;
gender? : 'm'|'f'
}
이렇게 되는 것과 마찬가지인것!
Required<T>
모든 프로퍼티를 필수로 바꿔준다
interface User{
id : number;
name : string;
age? : number;
gender : 'm'|'f'
}
//이렇게 인터페이스를 Required로 묶어주면 User에서 optional 프로퍼티로 정해놨던 age까지 필수로 입력해야함
let admin : Required<User> = {
id:1,
name:'Bob',
age : 30,
}
Readonly<T>
프로퍼티를 읽기전용으로 바꾼다
interface User{
id : number;
name : string;
age? : number;
gender : 'm'|'f'
}
//이렇게 인터페이스를 Readonly로 묶어주면 할당만 가능하고 값 수정은 불가능해진다
let admin : Readonly<User> = {
id:1,
name:'Bob',
age : 30,
}
Record<K,T>
K는 key , T는 type
const score : Record<'1'|'2'|'3'|'4','A'|'B'|'C'|'F'> = {
1:'A',
2:'C',
3:'F',
4:'B',
}
type Grade = '1'|'2'|'3'|'4';
type Score = 'A'|'B'|'C'|'F';
const score : Record<Grade,Score> = {
1:'A',
2:'C',
3:'F',
4:'B',
}
이렇게도 사용 가능
interface User{
id : number;
name : string;
age : number;
}
// 유저 정보가 유효한지를 boolean으로 반환하는 함수
function isValid(user:User){
const result : Record<keyof User,boolean> = {
id : user.id >0,
name : user.name !== "",
age: user.age >0,
}
return result;
}
Pick<T,K>
T타입에서 K프로퍼티만 골라서 사용한다
interface User{
id : number;
name : string;
age : number;
}
// User에서 id와 name만 사용
const admin : Pick<User,'id'|'name'> = {
id : 0;
name : 'Bob';
}
Omit<T,K>
T타입에서 K프로퍼티만 제외하고 사용한다
interface User{
id : number;
name : string;
age : number;
gender : 'M' | 'F'
}
// User에서 age와 gender만 빼고 사용
const admin : Omit<User,'age'|'gender'> = {
id : 0;
name : 'Bob';
}
Exclude<T1,T2>
T1에서T2를 제외하고 사용하는 방식, Omit과 다른점은 Omit은 프로퍼티로 제외하는 것이고, Exclude는 타입으로 제외하는 방식
type T1 = string | number | boolean;
type T2 = Exclude<T1,number|string> // 이러면 boolean만 사용
NonNullable<Type>
null을 제외한 타입 생성 null뿐만 아니라 undefined도 함께 제외시킨다.
type T1 = string | null | undefined | void;
type T2 = NonNullable<T1> // string과 void만 남는다.