Woongjae Lee
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team
2woongjae@gmail.com
2woongjae@gmail.com
console.log(hoisted_var);
var hoisted_var = '변수를 아래서 선언했는데 사용이 위에서 가능';
////////////////////////////////////////
console.log(hoisted_let);
let hoisted_let = '변수를 아래서 선언했는데 사용이 위에서 불가';
var redeclare_var: string = '한번 선언 했는데';
var redeclare_var: string = '또 선언이 가능';
// var redeclare_var: number = 0; (X)
////////////////////////////////////////
let redeclare_let = '한번 선언 했기 때문에';
let redeclare_let = '또 선언이 불가';
/*
그렇지만 var 에서 재선언 하더라도 같은 타입이어야 함.
*/
let a: string = '에이';
let b = '비이';
const c: string = '씨이';
const d = '디이';
/*
1. a 는 명시적으로 지정된 타입인 string
2. b 는 타입추론에 의한 타입인 string
3. c 는 명시적으로 지정된 타입인 string
4. d 는 타입추론에 의한 타입인 리터럴타입 "디이"
*/
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
/*
1. 주로 넓은 타입에서 좁은 타입으로 강제하는 경우가 많다.
2. jsx 에서는 as 를 쓴다.
*/
인터페이스랑 비슷해 보입니다.
Primitive, Union Type, Tuple
기타 직접 작성해야하는 타입을 다른 이름을 지정할 수 있습니다.
만들어진 타입의 refer 로 사용하는 것이지 타입을 만드는것은 아닙니다.
type MyStringType = string;
const str = 'world';
let myStr: MyStringType = 'hello';
myStr = str;
/*
별 의미가 없다..
*/
let person: string | number = 0;
person = 'Mark';
type StringOrNumber = string | number;
let another: StringOrNumber = 0;
another = 'Anna';
/*
1. 유니온 타입은 A 도 가능하고 B 도 가능한 타입
2. 길게 쓰는걸 짧게
*/
let person: [string, number] = ['Mark', 35];
type PersonTuple = [string, number];
let another: PersonTuple = ['Anna', 24];
/*
1. 튜플 타입에 별칭을 줘서 여러군데서 사용할 수 있게 한다.
*/
type Alias = { num: number }
interface Interface {
num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
/*
1. type alias 는 object literal type 로
2. interface 는 interface 로
*/
type PersonAlias = {
name: string;
age: number;
};
interface IPerson extends PersonAlias {
}
let ip: IPerson = {
name: 'Mark',
age: 35
};
class PersonImpl implements PersonAlias {
name: string;
age: number;
hello() {
console.log('안녕하세요');
}
}
let pi: PersonImpl = new PersonImpl();
pi.hello();
class PersonChild extends PersonAlias {
}
/*
1. 당연한건 type alias 끼리는 extends, implements 불가
2. interface extends type alias 가능
3. class implements type alias 가능
4. class extends type alias 블가 (interface 도 마찬가지)
5. 마치 interface 처럼 동작한다.
*/
function hello(person: { name: string; age: number; }): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p: { name: string; age: number; } = {
name: 'Mark',
age: 35
};
hello(p); // 안녕하세요! Mark 입니다.
///////////////////////////////////////////////////////////////
interface Person {
name: string;
age: number;
}
function hello(person: Person): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p: Person = {
name: 'Mark',
age: 35
};
hello(p); // 안녕하세요! Mark 입니다.
interface Person {
name: string;
age?: number;
}
function hello(person: Person): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p1: Person = {
name: 'Mark',
age: 35
};
const p2: Person = {
name: 'Anna'
};
hello(p1); // 안녕하세요! Mark 입니다.
hello(p2); // 안녕하세요! Anna 입니다.
interface Person {
name: string;
age?: number;
[props: string]: any;
}
function hello(person: Person): void {
console.log(`안녕하세요! ${person.name} 입니다.`);
}
const p1: Person = {
name: 'Mark',
age: 35,
};
const p2: Person = {
name: 'Anna',
systers: [
'Sung',
'Chan'
]
};
const p3: Person = {
name: 'Bokdaengi',
father: p1,
mother: p2
};
hello(p1); // 안녕하세요! Mark 입니다.
hello(p2); // 안녕하세요! Anna 입니다.
hello(p3); // 안녕하세요! Bokdaengi 입니다.
interface Person {
name: string;
age: number;
hello(): void;
}
const p1: Person = {
name: 'Mark',
age: 35,
hello: function (): void {
console.log(this);
console.log(`안녕하세요! ${this.name} 입니다.`);
}
};
const p2: Person = {
name: 'Mark',
age: 35,
hello(): void {
console.log(this);
console.log(`안녕하세요! ${this.name} 입니다.`);
}
};
const p3: Person = {
name: 'Mark',
age: 35,
hello: (): void => {
console.log(this);
console.log(`안녕하세요! ${this.name} 입니다.`);
}
};
p1.hello(); // 안녕하세요! Mark 입니다.
p2.hello(); // 안녕하세요! Mark 입니다.
p3.hello(); // 안녕하세요! 입니다.
interface IPerson {
name: string;
age?: number;
hello(): void;
}
class Person implements IPerson {
name: string;
constructor(name: string) {
this.name = name;
}
hello(): void {
console.log(`안녕하세요! ${this.name} 입니다.`);
}
}
const person = new Person('Mark');
person.hello(); // 안녕하세요! Mark 입니다.
interface Person {
name: string;
age?: number;
}
interface Korean extends Person {
city: string;
}
const k: Korean = {
name: '이웅재',
city: '서울'
};
interface HelloPerson {
// (name: string, age: number): void;
(name: string, age?: number): void;
}
let helloPerson: HelloPerson = function (name: string) {
console.log(`안녕하세요! ${name} 입니다.`);
};
helloPerson('Mark'); // 안녕하세요! Mark 입니다.
/*
함수의 타입 체크는 할당할때가 아니라 사용할때 한다는 점을 명심
*/
interface StringArray {
[index: number]: string;
}
const sa: StringArray = {}; // 옵셔널하다
sa[100] = '백';
interface StringDictionary {
[index: string]: string;
}
const sd: StringDictionary = {}; // 옵셔널하다
sd.hundred = '백';
interface StringArrayDictionary {
[index: number]: string;
[index: string]: string;
}
const sad: StringArrayDictionary = {};
// 당연히 옵셔널하다.
sad[100] = '백';
sad.hundred = '백';
interface StringDictionary {
[index: string]: string;
name: string;
}
const sd: StringDictionary = {
name: '이름' // 필수
};
sd.any = 'any'; // 어떤 프로퍼티도 가능
////////////////////////////////////////////////
interface StringDictionaryNo {
[index: string]: string;
// name: number; // (X) 인덱서블 타입이 string 값을 가지기 때문에 number 를 필수로 끌어오면 에러
}
class Person {
name: string;
age: number;
}
const person: Person = new Person();
console.log(person); // Person {}
person.age = 35;
console.log(person.name); // undefined
/*
1. 생성자 함수가 없으면, 디폴트 생성자가 불린다.
2. 클래스의 프로퍼티 혹은 멤버 변수가 정의되어 있지만, 값을 대입하지 않으면 undefined 이다.
=> 오브젝트에 프로퍼티가 아예 존재하지 않는다.
3. 접근제어자 (Access Modifier) 는 public 이 디폴트 이다.
*/
class Person {
name: string;
age: number;
constructor() {
console.log(this.name === null); // false
console.log(this.name === undefined); // true
}
}
const person: Person = new Person();
person.name = 'Mark';
person.age = 35;
console.log(person); // Person {name: 'mark', age: 35}
class Person {
name: string = 'Mark';
age: number = 35;
constructor() {
console.log(this.name); // 'mark'
}
}
const person: Person = new Person();
console.log(person); // Person {name: 'Mark', age: 35}
/*
1. 클래스의 프로퍼티를 선언과 동시에 값을 할당하는 방법도 있다.
2. 생성자가 불리기 전에 이미 프로퍼티의 값이 저장되어 있음을 알 수 있다.
*/
class Person {
public name: string;
private _age: number;
constructor(age: number) {
this._age = age;
}
}
const person: Person = new Person(35);
person.name = 'Mark';
// person._age (X)
console.log(person); // Person {name: 'Mark', _age: 35}
/*
1. private 으로 설정된 프로퍼티는 dot 으로 접근할 수 없다.
2. 클래스 내부에서는 private 프로퍼티를 사용할 수 있다.
3. private 이 붙은 변수나 함수는 _ 를 이름앞에 붙이는데,
이는 문법이 아니라 널리 쓰이는 코딩 컨벤션이다.
*/
class Parent {
private privateProp: string;
protected protectedProp: string;
constructor() {
}
}
class Child extends Parent {
constructor() {
super();
this.protectedProp = 'protected';
// this.privateProp = 'private'; // (X)
}
}
/*
1. 부모에서 private 으로 설정된 프로퍼티는 상속을 받은 자식에서도 접근할 수 없다.
2. 부모에서 protected 로 설정된 프로퍼티는 상속을 받은 자식에서 접근이 가능하다.
3. 상속을 받은 자식 클래스에서 부모 클래스에 this 를 통해 접근하려면, 생성자에서 super(); 를 통해 초기화 해야한다.
*/
class Person {
public name: string;
private _age: number;
constructor(age: number) {
this._age = age;
}
}
const person: Person = new Person();
/*
1. 디폴트 생성자는 프로그래머가 만든 생성자가 없을 때 사용할 수 있다.
=> 사용자가 만든 생성자가 하나라도 있으면, 디폴트 생성자는 사라진다.
*/
class Person {
constructor(private _name: string, private _age: number) { }
print(): void {
console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
}
printName = (): void => {
console.log(`이름은 ${this._name} 입니다.`);
}
private printAge(): void {
console.log(`나이는 ${this._age} 살 입니다.`);
}
}
const person: Person = new Person('Mark', 35);
person.print(); // 이름은 Mark 이고, 나이는 35 살 입니다.
person.printName(); // 이름은 Mark 입니다.
// person.printAge(); // (X)
/*
1. 클래스 내부에 작성된 메서드는 public 이 디폴트
2. arrow function 으로 작성 가능
3. private 을 이용하면 클래스 외부애서 접근 불가
*/
class Parent {
constructor(protected _name: string, protected _age: number) { }
print(): void {
console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
}
printName = (): void => {
console.log(`이름은 ${this._name} 입니다.`);
}
private printAge(): void {
console.log(`나이는 ${this._age} 살 입니다.`);
}
}
class Child extends Parent {
_name = 'Mark Jr.';
}
// const p: Child = new Child(); // (X)
const p: Child = new Child('', 5);
p.print(); // 이름은 Son 이고, 나이는 5 살 입니다.
/*
1. 상속은 extends 키워드를 이용한다.
2. 자식 클래스에서 디폴트 생성자는 부모의 생성자와 입력 형태가 같다.
*/
class Parent {
constructor(protected _name: string, private _age: number) { }
print(): void {
console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
}
protected printName = (): void => {
console.log(`이름은 ${this._name} 입니다.`);
}
protected printAge(): void {
console.log(`나이는 ${this._age} 살 입니다.`);
}
}
class Child extends Parent {
constructor(age: number) {
super('Mark Jr.', age);
this.printName();
this.printAge();
}
}
const p: Child = new Child(1);
// 이름은 Son 입니다.
// 나이는 1 살 입니다.
/*
1. 생성자를 정의하고, this 를 사용하려면, super 를 통해 부모의 생성자를 호출해줘야 한다.
2. super 를 호출할때는 부모 생성자의 입력 타입이 같아야 한다.
3. super 를 호출하는 것은 클래스 외부에서 호출하는 것과 같다.
4. protected 함수를 호출해서 그 안의 private 을 출력하는 것에 주의한다.
*/
class Person {
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
get name() {
return this._name;
}
set name(name: string) {
// 작업
this._name = `${name} Lee`;
}
}
const person: Person = new Person('Mark', 35);
console.log(person.name);
person.name = 'Woongjae';
console.log(person.name);
/*
1. _ 를 변수명 앞에 붙이고, 내부에서만 사용한다.
2. getter 를 함수처럼 설정하면, 프로퍼티처럼 꺼내쓸수있다.
3. 마찬가지로 setter 를 함수처럼 설정하면, 추가 작업을 하고 셋팅할 수 있다.
*/
class Person {
public static CITY = "";
private static lastName: string = 'Lee';
private _name: string;
private _age: number;
constructor(name: string, age: number) {
this._name = name;
this._age = age;
}
public print() {
console.log(`${this._name} ${Person.lastName} in ${Person.CITY}.`);
}
}
const person: Person = new Person('Mark', 35);
Person.CITY = 'Seoul';
person.print(); // Mark Lee in Seoul.
/*
1. static 키워드를 붙힌 프로퍼티는 클래스.프로퍼티로 사용한다.
2. static 프로퍼티에 private, protected 를 붙히면 똑같이 동작한다.
*/
class Person {
public static Talk(): void {
console.log('안녕하세요.');
}
}
Person.Talk(); // 안녕하세요.
class Person {
private static PROPERTY = '프라이빗 프로퍼티';
private static METHOD() {
console.log('프라이빗 메서드');
}
constructor() {
console.log(Person.PROPERTY);
Person.METHOD();
}
}
//////////////////////////////////////////////
const PROPERTY = '모듈 내 변수';
function METHOD() {
console.log('모듈 내 함수');
}
export class Person {
constructor() {
console.log(PROPERTY);
METHOD();
}
}
abstract class APerson {
protected _name: string = 'Mark';
abstract setName(name: string): void;
}
class Person extends APerson {
setName(name: string): void {
this._name = name;
}
}
// const person = new APerson(); // (X)
const person = new Person();
/*
1. abstract 키워드가 사용된 클래스는 new 로 생성할 수 없다.
2. abstract 키워드가 사용된 클래스를 상속하면 abstract 키워드가 붙은 함수를 구현해야 한다.
*/
class Preference {
private constructor() {
}
}
// const p: Preference = new Preference(); (X)
/*
1. 생성자 함수 앞에 접근제어자인 private 을 붙일 수 있다.
2. 외부에서 생성이 불가능하다.
*/
class Preference {
public static getInstance() {
if (Preference.instance === null) {
Preference.instance = new Preference();
}
return Preference.instance;
}
private static instance: Preference = null;
private constructor() {
}
}
const p: Preference = Preference.getInstance();
/*
1. private 생성자를 이용해서 내부에서만 인스턴스 생성이 가능하도록 함.
2. pubilc static 메서드를 통해 private static 인스턴스 레퍼런스를 획득한다.
3. Lazy Loading (Initialization) : 최초 실행시가 아니라, 사용시에 할당을 함
*/
class Person {
private readonly _name: string = null;
public readonly age: number = 35;
constructor(name: string) {
this._name = name;
}
public setName(name: string) {
// this._name = name; (X)
}
}
const p: Person = new Person('Mark');
console.log(p.age);
// p.age = 36; // (X)
/*
1. private readonly 로 선언된 경우, 생성자에서는 할당이 가능하다.
2. private readonly 로 선언된 경우, 생성자 이외에서는 할당이 불가능하다.
3. public readonly 로 선언된 경우, 클래스 외부에서는 다른값을 할당할 수 없다.
4. 마치 getter 만 있는 경우와 같다.
*/
function Car(name) {
this.name = name;
this.speed = 0;
this.honk = function() {
console.log("부우우웅");
};
this.accelerate = function(speed) {
this.speed = this.speed + speed;
}
}
var car = new Car("BENZ");
car.honk();
console.log(car.speed);
car.accelerate(10);
console.log(car.speed);
var baseObject = {
width: 0,
length: 0
};
var rectangle = Object.create(baseObject);
rectangle.width = 8;
rectangle.length = 6;
rectangle.area = function() {
return this.width * this.length;
};
console.log(rectangle.area());
var person = {
_firstName: ""
};
// https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
// ES5 이상
Object.defineProperty(person, "firstName", {
get: function () {
return this._firstName;
},
set: function (value) {
if (value.length > 3) {
this._firstName = value;
}
else {
this._firstName = "";
}
},
enumerable: true,
configurable: true
});
console.log(person.firstName);
person.firstName = "Ma";
console.log(person.firstName);
person.firstName = "Maximilian";
console.log(person.firstName);
By Woongjae Lee
타입스크립트 한국 유저 그룹 기초 스터디 201705
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team