[TS 과제 챌린지] Form Validator - 트러블슈팅

조민호·2023년 5월 23일
0
post-custom-banner


String Literal 타입과 index signature 사용하기

상태값을 사용하는데 있어서 객체 형태로 사용할 때,

type status = {
  username: boolean;
  email: boolean;
  password: boolean;
  password2: boolean;
};

const validateStatus: status = {
    username: false,
    email: false,
    password: false,
    password2: false,
};

이렇게 에러가 발생했다

저기서 input.id는 input태그의 id속성값을 가져온 것이다

즉, email이라는 문자열이다

<input type="text" id="email" placeholder="Enter email" />

JS방식대로 email이라는 문자열을 가져와서

문자열을 통해 객체의 프로퍼티에 접근하는 것인데

TS에서는 기본적으로 문자열을 키값으로 사용하지 않기에 에러가 발생한 것이다


그러므로 index signature을 사용해서 문제를 해결했다

type status = {
  [key: string]: boolean; //index signature
};



input태그에 접근할 때 필요한 타입

  1. JS의 경우, input태그를 document.getElementById() 같은 것을 통해서 해당 요소를 가져온 다음에
  2. 그 요소의 값에 접근하려면 .value 속성을 사용하면 됩니다
// HTML
<input type="text" id="username" placeholder="Enter username" />

// JS
const username = document.getElementById('username');
console.log(username.value) // 해당 input태그의 내용이 출력

TS에서는 input태그에 대해 .value 속성을 사용하기 위해선
타입을 HTMLElement 대신 , HTMLInputElement로 해야 합니다

// HTML
<input type="text" id="username" placeholder="Enter username" />

// TS
// Non-null Assertion 필수
const username:HTMLElement = document.getElementById('username')!;
console.log((username as HTMLInputElement).value) // 해당 input태그의 내용이 출력



하나의 타입으로 일관성있게 사용하기

공백을 체크하고 이에 따른 에러를 보여주는 함수가 있습니다

const blankStatus = {
    username: false,
    email: false,
    password: false,
    password2: false
};

const showBlankError = (blankStatus) => {
  for (let i in blankStatus) {
    let inputName = i;
    if (blankStatus[inputName] === false) {
      switch (inputName) {
        case 'username':
          inputName = username;
          break;
        case 'email':
          inputName = email;
          break;
        case 'password':
          inputName = password;
          break;
        case 'password2':
          inputName = password2;
          break;
      }
      showError(inputName, '공백입니다');
      continue;
    }
  }
};
  1. 간단히 설명하자면 , blankStatus 라는 상태가 있고

    이 상태는 각 input창에 대해서 공백인지 , 값이 들어가 있는지에 대한 상태를 나타냅니다

    const blankStatus: status = {
        username: false,
        email: false,
        password: false,
        password2: false,
      };
  2. blankStatus에서 값이 false인 키를 찾게 되면

    ( 각 키들은 getElementById로 가져온 DOM객체들을 의미한다고 가정합니다 )

  3. 이 DOM객체를 showError()로 넘겨주는 것입니다

💡 그렇지만 이 코드는 JS에서는 잘 돌아가지만 실제로는 매우 위험한 코드입니다
  • 현재 for .. in 을 통해서 blankStatus를 순회하며 각 키값들을 문자열 형태로 inputName라는 변수에 넣어주고 있습니다
  • 그리고 inputName의 키에 대한 값이 fasle라면 inputName에다가 DOM객체를 넣어줍니다

    즉, inputName이라는 변수에 문자열 넣어줬다가
    DOM객체를 넣어주고 있는 것입니다

그러므로 TS를 통해 아래와 같이 수정을 진행합니다

type status = {
  [key: string]: boolean; //index signature
};

const blankStatus: status = {
    username: false,
    email: false,
    password: false,
    password2: false
};

const showBlankError = (blankStatus: status) => {
  for (let i in blankStatus) {
    let inputName: string = i;
    let errorTarget: HTMLElement | undefined; **// 추가**
    if (blankStatus[inputName] === false) {
      switch (inputName) {
        case 'username':
          errorTarget = username;
          break;
        case 'email':
          errorTarget = email;
          break;
        case 'password':
          errorTarget = password;
          break;
        case 'password2':
          errorTarget = password2;
          break;
      }
      showError(errorTarget as HTMLInputElement, '공백입니다');
      continue;
    }
  }
};

inputName은 그대로 문자열 형태로 사용하되,

errorTarget라는 DOM객체를 받는 변수를 따로 선언합니다

현재 switch문에는 default가 없으므로 ts컴파일러는
(switch문의 조건들에 걸리지 않을 경우)errorTarget에 값이 할당되지 않을 수도 있다며 에러를 뱉으므로 errorTarget의 타입을 HTMLElement 가 아닌 HTMLElement | undefined 로 해줍니다




as HTMLElement는 지양하기

보통 DOM객체를 받아올때 동적으로 바뀌는 부분이 아니라면,

HTMLElement라는 타입으로 바로 받아오는 것보다 최대한 타입을 상세히 작성하는 것이 좋습니다

  • bad
document.getElementById('form') as HTMLElement;
document.getElementById('username') as HTMLElement;
document.getElementById('email') as HTMLElement;
document.getElementById('password') as HTMLElement;
  • good
document.getElementById('form') as HTMLFormElement;
document.getElementById('username') as HTMLInputElement;
document.getElementById('email') as HTMLInputElement;
document.getElementById('password') as HTMLInputElement;
post-custom-banner

0개의 댓글