본문 바로가기

배워서 따라하는 포플/포켓몬 도감 앱

타입스크립트로 변경하기(2)

BaseStat.tsx 타입 변경

interface BaseStatProps {
  valueStat: number;
  nameState: string;
  type: string;
}

components폴더 안에 BaseStat.tsx에 타입을 명시합니다.

에러난 부분의 타입을 명시해주면 됩니다.

 

Damage Relations 컴포넌트 타입 변경

import { DamageRelations as DamageRelationsProps } from '../types/DamageRelationOfPokemonTypes';

interface DamageModalProps {
  damages: DamageRelationsProps[];
  setIsModalOpen: React.Dispatch<React.SetStateAction<boolean>>
}

DamageModal.tsx 파일에 타입을 명시합니다.

에러난 부분의 타입을 명시해주면 됩니다.

 

export interface DamageFromAndTo {
  to: SeparateDamages;
  from : SeparateDamages;
}

export interface SeparateDamages {
  double_damage?: Damage[];
  half_damage?:   Damage[];
  no_damage?:     Damage[];
}

export interface Damage {
  damageValue: string;
  name:        string;
  url:         string;
}

types폴더안에 SeparateDamageRelation.ts파일을 위와 같이 작성합니다.

 

import { DamageRelations as DamageRelationsProps } from '../types/DamageRelationOfPokemonTypes';
import { Damage, DamageFromAndTo, SeparateDamages } from '../types/SeparateDamageRelations';

interface DamageModalProps {
  damages: DamageRelationsProps[];
}

interface Info {
  name: string;
  url: string;
}

DamageRelations.tsx 파일에 타입을 명시합니다.

const DamageRelations = ({damages}: DamageModalProps) => {

  const [damagePokemonForm, setDamagePokemonForm] = useState<SeparateDamages>()  

  useEffect(() => {
    const arrayDamage = damages.map((damage) => separateObjectBetweenToAndFrom(damage))

    // type이 2개인지 1개인지 구분해서 데이터 가공
    if(arrayDamage.length === 2) {
      const obj = joinDamageRelations(arrayDamage)
      setDamagePokemonForm(reduceDuplicateValues(postDamageValue(obj.from)))
    } else{
      setDamagePokemonForm(postDamageValue(arrayDamage[0].from))
    }
  }, [damages])
  
  const joinDamageRelations = (props: DamageFromAndTo[]): DamageFromAndTo => {
    return {
      to: joinObjects(props, 'to'),
      from: joinObjects(props, 'from'),
    }
  }

  const reduceDuplicateValues = (props: SeparateDamages) => {
    const duplicateValues = {
      double_damage: '4x',
      half_damage: '1/4x',
      no_damage: '0x'
    }

    return Object.entries(props)
      .reduce((acc, [keyName, value]) => {
        const key = keyName as keyof typeof props
        const verifiedValue = filterForUniqueValues(
          value,
          duplicateValues[key]
        )

        return (acc = { [keyName]: verifiedValue, ...acc})
      }, {})
  }

  const filterForUniqueValues = (valueForFiltering: Damage[], damageValue: string) => {
    const initialArray: Damage[] = []

    return valueForFiltering.reduce((acc, currentValue) => {
      const {url, name} = currentValue

      const filterACC = acc.filter((a) => a.name !== name)

      return filterACC.length === acc.length
        ? (acc = [currentValue, ...acc])
        : (acc = [{damageValue: damageValue, name, url}, ...filterACC])
    }, initialArray)
  }

  const joinObjects = (props: DamageFromAndTo[], string: string) => {
    const key = string as keyof typeof props[0]
    const firstArrayValue = props[0][key]
    const secondArrayValue = props[1][key]

    const result = Object.entries(secondArrayValue)
      .reduce((acc, [keyName, value]) => {
        const key = keyName as keyof typeof firstArrayValue
        const result = firstArrayValue[key]?.concat(value)
        return (acc =  {[keyName]: result, ...acc})
      }, {})
    
    return result
  }

  const postDamageValue = (props: SeparateDamages): SeparateDamages => {
    const result = Object.entries(props)
      .reduce((acc, [keyName, value]) => {
        const key = keyName as keyof typeof props
        const valuesOfKeyName = {
          double_damage: '2x',
          half_damage: '1/2x',
          no_damange: '0x'
        }

        return (acc = {
          [keyName]: value.map((i: Info[]) => ({
            damageValue: valuesOfKeyName[key],
            ...i
          })),
          ...acc
        })
      }, {})

    return result
  }
  
  // 데미지 from과 to 분리
  const separateObjectBetweenToAndFrom = (damage: DamageRelationsProps): DamageFromAndTo => {
    const from = filterDamageRelations('_from', damage)
    const to = filterDamageRelations('_to', damage)
    return {from, to}
  }

  const filterDamageRelations = (valueFilter: string, damage: DamageRelationsProps) => {
    const result: SeparateDamages = Object.entries(damage)
      .filter(([keyName, value]) => {
        return keyName.includes(valueFilter)
      })
      .reduce((acc, [keyName, value]): SeparateDamages => {
        const keyWithValueFilterRemove = keyName.replace(valueFilter, '')
        return (acc = {[keyWithValueFilterRemove]: value, ...acc})
      }, {})
    return result
  } 

  return (
    <div className='flex gap-2 flex-col'>
      {damagePokemonForm ? (
        <>
          {Object.entries(damagePokemonForm)
            .map(([keyName, value]: [string, Damage[]]) => {
              const key = keyName as keyof typeof damagePokemonForm
              const valuesOfKeyName = {
                double_damage: 'Weak',
                half_damage: 'Resistant',
                no_damage: 'Immune'
              }

              return (
                <div key={key}>
                  <h3 className='capitalize font-medium text-sm md:text-base text-slate-500 text-center'>
                    {valuesOfKeyName[key]}
                  </h3>
                  <div className="flex flex-wrap gap-1 justify-center">
                    {value.length > 0 ? (
                      value.map(({name, url, damageValue}) => {
                        return (
                          <Type type={name} key={url} damageValue={damageValue} />
                        )})
                    ) : (
                      <Type type={'none'} key={'none'} />
                    )}
                  </div>
                </div>
              )
            })}
        </>
      ) : <div></div>
      }
    </div>
  )
}

export default DamageRelations

DamageRelations.tsx 파일에 에러 부분 전체 수정합니다.

여기서 Type컴포넌트도 같이 수정해야 합니다.

interface TypeProps {
  type: String;
  damageValue?: String;
}

Type.tsx에 타입 명시합니다.

에러난 부분 수정합니다.

 

로그인 페이지 타입 변경

LoginPage폴더 안에 index.tsx 이름 변경합니다.

루트 경로에 App.tsx, firebase.ts 이름만 변경합니다.

NavBar.tsx는 위와 같이 수정합니다.

 

.d.ts 선언 파일 생성하기

어떠한 객체가 있는지 그 객체에 유니크한 식별자를 주기 위해서 UUID라는 라이브러리를 이용해서 유니크한 값을 만들어보겠습니다.

npm install uuid

위 명령어로 UUID 설치합니다.

import {v4} from 'uuid'

위 코드로 uuid를 import합니다. 여기에 타입 에러가 나타납니다. 에러 사항은 uuid의 패키지json에서 .js로 끝나는 파일을 지정해서 유니크한 식별자를 부여했기때문입니다. 

그래서 .d.ts 파일에서 기타 파일도 식별자를 부여할 수 있게 선언이 필요합니다.

declare module 'uuid' {
  const v4: () => string;
  export {v4}
}

src폴더 안에 uuid.d.ts파일을 생성합니다. 위와 같이 작성하면 import시 에러 없습니다.

만약에 .d.ts파일의 이름이 uuid가 아니고 다른 이름으로 작성했으면 에러 뜹니다.

이게 uuid라는 이름인 .d.ts파일만 검색해서 그렇습니다.

/// <reference path="./a.d.ts" />

위와 같이 a라는 이름으로 .d.ts파일을 생성했다면 경로를 알려줘야 합니다.

 

https://github.com/DefinitelyTyped/DefinitelyTyped

 

.d.ts파일 생성 말고도 위 깃허브 링크에서 웬만한 모듈을 위한 타입들을 이미 만들어졌습니다.

README파일을 참조해서 사용하면 됩니다.