4. typescript - 변수 선언

2020. 2. 29. 00:13typescript/typescript-grammar

변수 선언 

let과 const는 js에서 상대적으로 새로나온 변수 선언 방법입니다. let은 var과 비슷하지만 재선언 등을 사용하지 못합니다. 그리고 const는 한번 적용한 값을 변환시킬 수 없는 변순 선언 법입니다. 

 

typescript는 js를 포함하고 있기 때문에 당연하게 let, const를 사용할 수 있습니다. 그래서 과거의 var를 사용하는 것 보단 let과 

const를 사용하는 것이 더 바람직합니다. 

 

let 

var의 몇가지 문제 점들을 해결한 변수선언 방법이다. let은 var와 같은 방법으로 선언하여 사용할 수 있다. 

let hello = "Hello!";

 

block scope 

var는 function scope라서 함수를 기준으로 변수를 찾아와 사용하지만 let은 block scope {}을 기준으로 변수를 찾을 수 있습니다. 

 

let의 경우

function f(input: boolean) {
  let a = 100;

  if (input) {
    let b = a + 1;
    return b;
  }
  return b; Error // 다른 블록에 있어서 if 안에 들어있는 b를 반환하지 못한다.
}

 

var의 경우 

function f(input: boolean) {
  let a = 100;

  if (input) {
    var b = a + 1;
    return b;
  }
  return b; // var는 함수를 기준으로 scope를 구성하기 때문에 
} // 에러가 발생하지 않는다. 

 

또한 let고 const는 호이스팅이 안된다. 따라서 아래와 같은 구문은 되지 않는다.

x++;
let x = 1;

 

아래와 같은 경우 ES2015이후로는 원래 에러가 발생해야하지만 지금 당장의 typescript에서는 에러가 발생하지 않습니다. 

function foo() {
    // okay to capture 'a'
    return a;
}

// foo를 호출해주기 전에 a를 선언하는 것은 
// 에러가 발생할 것입니다.
foo();

let a;

 

재선언 

 

var의 경우 재선언을 해주어도 특별한 에러가 생기지 않았다. 아래와 같이 var의 경우에는 재선언을 허용한다. 

function f2(x) {
  var x;
  var x;
  if (true) {
    var x;
  }
}

 

하지만 let의 경우 아래와 같이 작성할 경우 에러가 발생한다. 

let x = 10;
let x = 20;

 

또는 아래와 같은 경우에도 에러가 발생한다. 

function f(x){
	let x = 100;
}

function g(){
	var x = 1;
    let x = 2; Error
}

 

그리고 아래와 같은 경우에 block을 다르게 scope를 구성하기 때문에 

function f3(condition, x) {
  if (condition) {
    let x = 100;
    return x;
  }
  return x;
}

f3(true, 0); // return 100
f3(false, 0); // return 0

 

 

Shadowing

 

shadowing은 우발적으로 에러를 발생시키기도 하고 예방시켜 주기도 합니다. 그래서 양날의 검을 갖고 있습니다. 아래와 같이 i를 외부와 내부의 scope를 만들어 정상적으로 실행할 수 있습니다. 

function sumMatrix(matrix: number[][]) {
  let sum = 0;
  for (let i = 0; i < matrix.length; i++) {
    var currentRow = matrix[i];
    for (let i = 0; i < currentRow.length; i++) {
      sum += currentRow[i];
    }
  }

  return sum;
}

 

Block-scoped variable capturing

 

아래의 경우 getCity()가 반환될 때 city를 capturing 해두었기 때문에 city의 block이 벗어났어도 에러없이 사용할 수 있습니다. 

function theCityThatAlwaysSleeps() {
  let getCity;

  if (true) {
    let city = "Seattle";
    getCity = function() {
      return city;
    };
  }

  return getCity();
}

 

Const

const도 이전과 다르지 않게 선언할 수 있습니다.

const numLivesForCat = 9;

let과 다르게 안의 값을 바꿀 수 없다. 그러나 scope의 기준이나 재할당을 할 수 없는 것은 같습니다.

 

그렇지만 const 변수의 내부 상태는 변경가능합니다.

const numLivesForCat = 9;
const kitty = {
    name: "Aurora",
    numLives: numLivesForCat,
}

// Error
kitty = {
    name: "Danielle",
    numLives: numLivesForCat
};

// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;

 

Destructuring(비구조화)

비구조화는 구조화된 배열을 분리하여 개별적인 변수에 할당할 수 있게 해주는 방법입니다. 

 

Array Destructuring

 

비구조화 할당을 통해 배열 값을 더 쉽게 나누어 줄 수 있다.

// Destructuring
let input = [1, 2];
let [first, second] = input;
console.log(first); // 1;
console.log(second); // 2;

// bad method
first = input[0];
second = input[1];

 

또한 아래와 같이 값도 쉽게 바꿀 수 있다.

[first, second] = [second, first];

그리고 함수에서 파라미터로 사용할 수 있습니다. 

function f([first, second]: [number, number]) {
  console.log(first);
  console.log(second);
}

f([1, 2]);

 

...구문을 사용하여 하나의 변수에 나머지의 값을 할당할 수 있습니다. 

const [third, ...forth] = [1, 2, 3, 4];
console.log(third); // 1
console.log(forth); // [2, 3, 4]

 

또는 특정 값을 선택하여 받거나 한 변수에 하나의 값만을 넣을 수 있습니다. 

let [first] = [1, 2, 3, 4];
console.log(first);

let [, second, , forth] = [ 1, 2, 3, 4];
console.log(second); // 2
console.log(forth); // 4

 

tuple destructuring

 

tuple도 위와 같이 비구조화할당을 할 수 있습니다. 

let tuple: [number, string, boolean] = [7, "Hello", false];
let [a, b, c] = tuple; //a number, b string, c boolean

 

하지만 개수를 맞춰주지 않으면 에러가 발생합니다. 

let [a, b, c, d] = tuple; // Error  no element at index 3

배열과 같이 ...을 사용할 수 있다.

let [a, ...b] = tuple; //b는 false와 Hello가 들어간다.

 

Object destructuring

 

객체또한 비구조화할당을 할 수 있다. 

let o = {
  a: "foo",
  b: 12,
  c: "bar"
};

let { a, b } = o; // 필요 없는 값인 c등을 무시할 수 있다. 

json형태에서 사용하기 위해서는 o의 key 값과 동일하게 맞추어 주어야합니다. 

 

let { a, ...fire } = o;
console.log(fire.b, fire.c);

a를 제외한 나머지는 ...을 사용하여 받을 수 있습니다. 

 

property destructuring

json을 비구조화 할당으로 받을 때 key의 이름이 아닌 다른 이름으로 받을 수 있다. 기존방법으로는 두줄을 사용해야하지만 이 방법을 사용하면 한줄로 마무리할 수 있다. 

// 요소 이름 변환
let { a: newName1, b: newName2 } = o;

// 기존 방법
let newName1 = o.a;
let newName2 = o.b;

 

비구조화 할당을 할 때 특정한 타입을 지정하고 있지 않은데 타입을 지정해주는 것이 다른 사람이 보기에 안헷갈리고 좋다. 

let { a, b } : {a:string, b:number} = o;

 

default values

function keepWholeObject(wholeObject: { a: string, b?: number }) {
    let { a, b = 1001 } = wholeObject;
}

?여기서 물음표는 b의 값을 선택적으로 넣었다 뺐다 해도 된다는 의미입니다. 그리고 아래의 비구조화할당에서 b에 아무값도 대입이 안되면 default로 1001을 대입한다는 의미입니다. 

 

Function declarations 

함수의 파라미터를 설정하는데도 비구조화를 사용할 수 있습니다. 

type C = { a: string; b?: number };
function f6({ a, b }: C): void {}

그리고 trick과 같이 오른쪽의 {}를 사용하는 패턴을 기억해야 합니다. 

function f({ a="", b=0 } = {}): void {}
f();

 

그런 다음 기본 초기화 대신에 비구조화 프로퍼티를 추가적인 옵션으로 선택할 수 있게 만들어야합니다. 

function f8({ a, b = 0 } = { a: "" }): void {}

f8({ a: "yes" }); //ok default b = 0
f8(); // a= "",
f8({}); // error 인수를 제공하려면 a가 필수입니다.

 

사실 글을 작성하면서도 많이 혼란스러운 문법이다. 그래서 최대한 간단하고 기본적이게 유지하는 것이 좋을 것 같습니다. 

 

Spread

spread는 비구조화의 정반대의 개념이다. 배열에 있는 값을 다른 배열에 퍼트리는 것이 가능하다. 모든 값은 왼쪽부터 오른쪽까지 차례대로 spread 된다.

let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second];

object또한 spread가 가능하다.  

let defaults = { foot: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, foot: "rich" };

하지만 아래와 같이 key값이 동일한 경우 덮어 씌워집니다. 

let test = { sex: "man" };
let test2 = { sex: "woman", ...test };
console.log(test2); // man

 

그리고 객체를 spread하는데 큰 제한이 있는데 spread할 때 객체의 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties 여기 있는 것만이 spread됩니다. 즉 method는 잃게 됩니다. 

class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!

또 generic function을 spread하는 것을 인정하지 않습니다. 

 

'typescript > typescript-grammar' 카테고리의 다른 글

6. typescript - class  (0) 2020.03.06
5. typescript - interfaces  (0) 2020.03.03
3. typescript - 타입  (0) 2020.02.27
2. typescript - 개발환경 설정  (0) 2020.02.27
1. typescript - 특장점  (0) 2020.02.26