useState(””)
과 useState(””)
은 typescript 컴파일러가 인식할 때 둘 다 문제가 없다고 보는 것 같다.
음.. 차이점을 생각해보자.
string
타입: 소문자로 작성된 string
은 원시 자료형(primitive type)인 문자열을 나타내는 타입
이는 일반적인 문자열 값이다.
const myName: string = "John";
String
타입: 대문자로 시작하는 String
은 원시 자료형이 아닌 래퍼 객체(wrapper object)로서의 문자열을 나타내는 타입
이것은 일반 문자열 값을 감싸고 추가적인 속성과 메소드를 제공하는 객체이다.
한마디로 객체였던 거임..
쓰지 말자.
원시 데이터 유형에 대해 메서드를 호출하려면 JavaScript에서는 래퍼 객체를 사용한다.
래퍼 객체란 원시 값 주위에 생성된 객체로, 해당 원시 값을 감싸는 역할을 하게 된다. 주로 원시형 값에 대한 추가적인 메서드와 속성을 제공하기 위해 사용된다.
예를 들어, 문자열 레퍼 객체는 String
객체이고, 숫자 레퍼 객체는 Number
객체이다.
// 문자열 래퍼 객체
let str = "Hello";
let strObj = new String(str);
// 숫자 래퍼 객체
let num = 42;
let numObj = new Number(num);
console.log(str.length); // 문자열 래퍼 객체의 메서드 호출
console.log(numObj.toFixed(2)); // 숫자 래퍼 객체의 메서드 호출
근데 여기서 한 가지 기억할 점이 있다 !
자바스크립트는 필요에 따라 자동으로 원시값과 레퍼 객체를 변환하여 사용자가 편리하게 코드를 작성할 수 있게 해준다.
let str = "Hello";
let strLength = str.length; // 문자열이 원시형이지만 래퍼 객체가 자동으로 생성됨
그렇다고 str.length
가 호출된 순간 str 변수의 타입이 레퍼 객체로 변환되는 것은 아니다.
boolean
vs Boolean
boolean
: 원시 자료형으로 참 또는 거짓을 나타내는 값.Boolean
: 래퍼 객체로서 논리값을 나타내며 JavaScript의 Boolean
생성자 함수로 생성한다.const isTrue: boolean = true; // 원시 타입
const isTrueWrapper: Boolean = new Boolean(true); // 래퍼 객체
object
vs Object
object
: 모든 객체(비원시 값)의 타입Object
: 모든 객체의 부모 객체로서 Object
생성자 함수로 생성된 객체const myObj: object = { key: 'value' }; // 모든 객체 타입
const myObject: Object = new Object(); // Object 생성자로 생성된 객체
array
vs Array
array
: 원시 자료형이 아닌, JavaScript의 배열 객체Array
: 배열 객체를 생성하기 위한 생성자 함수로, 보통 배열 리터럴([])을 사용const myArr: array = [1, 2, 3]; // 배열 객체
const myArray: Array<number> = [1, 2, 3]; // 배열 생성자 함수로 생성된 객체
function
vs Function
function
: JavaScript의 함수 객체Function
: 함수를 생성하기 위한 생성자 함수로, 사용보다는 () => {}
와 같은 리터럴 함수 표현을 선호const myFunc: function = () => {}; // 함수 객체
const myFunction: Function = new Function(); // 함수 생성자 함수로 생성된 함수 객체 (권장하지 않음)
바로 값을 저장하는 방식의 차이가 있다.
먼저 원시 타입은 불변 형태의 값으로 저장된다. 그리고 이 값은 변수 할당 시점에 메모리 영역을 차지하고 저장된다.
반대로 객체는 프로퍼티를 삭제, 추가, 수정할 수 있으므로 원시 값과 다르게 변경 가능한 형태로 저장되며, 값을 복사할 때도 값이 아닌 참조가 전달된다. 때문에 다음과 같은 현상이 발생하는데 ...
const a = function() {}
const b = function() {}
a === b // false
객체 레퍼는 참조를 저장(값 저장 x, 가변할 수 있기 때문)하기 때문에 앞서 동일하게 선언한 객체라 하더라도 저장하는 순간 다른 참조를 본다. 따라서 위의 코드 처럼 동등 연산자 비교에서 false
가 나오는 것이다.
근데 왜 useState에서 레퍼 타입이랑 원시형 타입이랑 상관없다고 보는 걸까..?
일단 useState
를 분석해보자 .
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
1. 제네릭(Generic) 타입 S: <S>
는 제네릭 타입 S를 받아들이며, 이는 상태의 타입을 나타낸다.
2. 매개변수 initialState: useState
는 initialState라는 매개변수를 가지며, 이는 초기 상태를 나타냅니다. 초기 상태는 제네릭 타입 S 또는 () => S 형태일 수 있습니다. 만약 initialState
이 함수 형태인 경우, 해당 함수가 호출되어 반환된 값이 초기 상태로 사용된다.
3. 반환 값의 타입: [S, Dispatch<SetStateAction<S>>]
형태의 튜플(tuple)을 반환
4. S: 현재 상태의 타입
Dispatch<SetStateAction<S>>
: 상태를 갱신. SetStateAction
은 제네릭 타입 S 또는 상태를 갱신하는 함수 ((prevState: S) => S)
의 유니온 타입
이는 TypeScript가 명시적인 형변환 없이도 일반적인 경우에 타입을 유추하고 호환성을 제공하기 때문이다 .. !!
다음과 같은 코드가 있다고 해보자.
const bool1: boolean = true;
const bool2: Boolean = new Boolean(true);
const shouldBeBoolean1: boolean = bool2; // 호환됨
const shouldBeBoolean2: Boolean = bool1; // 호환됨
컴파일 에러가 나오지 않는다.. !! (참고로 vscode에서 자동으로 컴파일해서 타입체킹을 그때그때 해준다.)
다시 예를 들자면, Boolean
래퍼 객체와 boolean
원시 타입은 비슷하지만 다르다.
래퍼 객체에는 추가적인 메서드 등이 존재하므로 useState
에 넣고 사용했을 때 예상치 못한 오류가 나올 수 있다.
Typescript의 호환성 때문에 당장은 빨간 밑줄이 나오지는 않지만 조심하자.. !!