TypeScript가 내 시간을 낭비하고 있습니다.

⚠️ 이것은 제가 TypeScript에 대한 인내심을 잃었고 분출해야 하기 때문에 약간의 호언장담입니다.

중간 크기의 Nuxt 애플리케이션(~15페이지, i18n, auth, REST API)을 TypeScript로 변환하는 동안 문제점 목록(특정 순서 없음)을 컴파일했습니다. TS를 사용하면서 나를 비참하게 만든 것은 이번이 처음이 아닙니다. 아마도 이것은 "나"의 문제이고 나는 지식이나 기술이 부족할 것입니다. 그러나 이것이 사실이라면 많은 새로운 개발자들도 이러한 장애물에 부딪히고 TS를 둘러싼 과대 광고 때문에 아무 말도 하지 않을 것이라고 장담합니다.

null인가요?



이 간단한 "캐시 초기화"코드를 고려하십시오.

Object.keys(localStorage).forEach((key) => {
  store.commit('cache/init', {
    key,
    value: JSON.parse(localStorage.getItem(key)),
  });
});


Playground
localStorage의 모든 항목을 반복하고 구문 분석된 값을 저장소로 보냅니다.
여기에서 localStorage.getItem(key)JSON.parse를 첫 번째 인수로 받아들이고 stringlocalStorage.getItem를 반환할 수 있기 때문에 null에서 오류가 발생합니다. 알았어...하지만...아무거나! JSON.parse(null) 오류도 발생하지 않습니다. 설상가상으로 localStorage 의 기존 항목을 반복하고 있기 때문에 설정되지 않은 값을 가질 수 없습니다.

"콤팩트"는 "콤팩트"가 아닙니다.



이 숫자 포맷터 코드를 고려하십시오.

function formatNumber(value: number, lang: string = 'en') {
  const options = {
    notation: 'compact',
    maximumFractionDigits: 1,
  };
  const formatter = Intl.NumberFormat(lang, options);
  return formatter.format(value);
}


Playground
options 필드는 notation 여야 하는데 문자열이기 때문에 "compact" | "standard" | "scientific" | "engineering" | undefined 매개변수에 오류 밑줄이 그어져 있습니다. 글쎄요 ... 그것은 "compact" 로 하드 코딩되어 있습니다. 그것은 나에게 "compact" 에 매우 가깝습니다.

유형 IDontCare



Nuxt에서 다음 플러그인 선언을 고려하십시오.

export default (_, inject: Function) => {
  inject('myPlugin', /* Plugin code */);
};


Playground

Nuxt에서 플러그인은 2개의 매개변수로 호출됩니다. 첫 번째는 Nuxt 컨텍스트이고 두 번째는 해당 컨텍스트에 플러그인을 추가하는 기능입니다.
여기서는 컨텍스트를 사용하지 않으므로 권장 사항에 따라 _로 설정합니다. 그러나 암시적 유형any이 있기 때문에 오류가 발생합니다. 내 말은... 맞아, 하지만 누가 신경 쓰겠어? 이 매개변수를 사용하지 않습니다. 나는 그것을 사용하지 않는다는 것을 알리기 위해 특별히 이름을 변경했습니다. 오류로 보고되는 이유는 무엇입니까?

코드 중복!



이것은 나에게 꽤 불쾌합니다. 다시 Nuxt의 플러그인 선언을 고려하십시오. 이 플러그인은 일련의 기능을 노출합니다.

export default (_: DontCare, inject: Function) => {
  const API = {
    get(key: string): object { /* Code code code */ }
    set(key: string, value: object): void { /* Code code code */ }
  };
  inject('myPlugin', API);
};


거기까지 모든 것이 좋습니다. 이제 내 코드에서 사용하고 싶습니다. 주입된 함수를 가능한 모든 곳에 선언해야 합니다.

interface API {
    get(key: string): object
    set(key: string, value: object): void
}
declare module 'vue/types/vue' {
  // this.$myPlugin inside Vue components
  interface Vue {
    $myPlugin: API
  }
}

declare module '@nuxt/types' {
  // nuxtContext.app.$myPlugin inside asyncData, fetch, plugins, middleware, nuxtServerInit
  interface NuxtAppOptions {
    $myPlugin: API
  }
  // nuxtContext.$myPlugin
  interface Context {
    $myPlugin: API
  }
}

declare module 'vuex/types/index' {
  // this.$myPlugin inside Vuex stores
  interface Store<S> {
    $myPlugin: API
  }
}

export default (_: DontCare, inject: Function) => {
  const API: API = {
    get(key) { /* Code code code */ }
    set(key, value) { /* Code code code */ }
  };
  inject('myPlugin', API);
};


최악의 부분은 내가 해야 한다는 것조차 아닙니다tell TS that Nuxt is injecting my plugin everywhere . 최악의 부분은 플러그인의 모든 함수 서명이 인터페이스와 일치하는지 확인해야 한다는 것입니다. API 자체에서 유형을 유추할 수 없는 이유는 무엇입니까? 또한 ctrl + click는 구현이 아닌 인터페이스를 가리키기 때문에 쓸모 없게 됩니다(IDE 문제일 수도 있지만 여전히 ...).

가장 중요한 점은 인터페이스의 함수 매개변수가 사용되지 않는 것으로 간주되기 때문에 이제 ESlint가 불평하고 있다는 것입니다.

확장자 없이 가져오기



TS는 파일 유형을 감지하고 그에 따라 컴파일하기 위해 파일 확장자가 필요합니다. 충분하지만 이제 모든 가져오기를 수행하고 모든 곳에 .vue를 추가해야 합니다.

동적 인터페이스



문자열에 추가하기 위해 호출을 연결할 수 있는 URL 빌더가 있습니다.

const API = () => {
  let url = 'api';
  const builder = {
    currentUser() {
      return this.users('4321');
    },
    toString() {
      return url;
    }
  };
  ['users', 'articles', /* ... */].forEach((entity) => {
    builder[entity] = (id) => {
      url += `/${entity}${id ? `/${id}` : ''}`;
      return builder;
    };
  });
};

// Usage
const url = `${API().users('4321').articles()}`; // => 'api/users/4321/articles'


TS가 비명을 지르기 전까지는 괜찮습니다. 내 항목을 나열하는 유형을 선언하고 이 유형을 Record의 키로 사용할 수 있습니다(Code duplication ! 참조). 그러나 toStringcurrentUser 방법에 대해서도 따로 설명해야 합니다.

type Entities = 'users' | 'articles' | /* ... */;
type URLBuilder = Record<Entities, (id?: string) => URLBuilder> & {
  currentUser(): URLBuilder
  toString(): string
};

const API = () => {
    let url = 'api';
    const builder: URLBuilder = {
        currentUser() {
            return this.users('4321');
        },
        toString() {
            return url;
        }
    };

    const entities: Entities[] = ['users', 'articles'];
    entities.forEach((entity) => {
        builder[entity] = function (id?: string) {
            url += `/${entity}${id ? `/${id}` : ''}`;
            return this;
        }
    });

    return builder;
};


Playground

문제 해결됨 ? 그렇지 않습니다... 전체를 빌드하는 동안 초기화된 임시 빌더가 아직 유형이 아닙니다URLBuilder. 나는 "이것은 몇 줄 안에 T 유형이 될 것입니다"라고 말하는 방법을 모릅니다.

결론



나는 이러한 모든 문제가 약간의 지식 부족으로 인한 것이라고 절대적으로 확신합니다. 이들 중 하나에 대한 우아한 솔루션이 있으면 의견을 공유하십시오.

Microsoft는 시간을 낭비하는 일에 그렇게 많은 에너지를 투자하지 않습니다. 나는 몇 년 후에 이 기사로 돌아와서 이 모든 것이 우스꽝스럽다는 것을 발견하고 싶지만 지금은 Typescript에 대한 과대 광고를 받지 않습니다.

반겨주셔서 감사합니다 😎

좋은 웹페이지 즐겨찾기