programing

스위치 블록이 TypeScript에서 완전히 사용되고 있는지 확인하려면 어떻게 해야 합니까?

mytipbox 2023. 2. 22. 21:25
반응형

스위치 블록이 TypeScript에서 완전히 사용되고 있는지 확인하려면 어떻게 해야 합니까?

몇 가지 코드가 있습니다.

enum Color {
    Red,
    Green,
    Blue
}

function getColorName(c: Color): string {
    switch(c) {
        case Color.Red:
            return 'red';
        case Color.Green:
            return 'green';
        // Forgot about Blue
    }

    throw new Error('Did not expect to be here');
}

제가 깜빡 잊고Color.Blue컴파일 에러가 났으면 좋겠어요.TypeScript가 에러로서 플래그를 붙이도록 코드를 구조화하려면 어떻게 해야 합니까?

위해서는, 「이것들」을 사용합니다.never(TypeScript 2.0을 사용합니다).이것은, 「해서는 안 되는」값을 나타냅니다.

첫 번째 단계는 함수를 작성하는 것입니다.

function assertUnreachable(x: never): never {
    throw new Error("Didn't expect to get here");
}

다음 ' 때 보다'에서 사용하세요.default( 는또 ) :

function getColorName(c: Color): string {
    switch(c) {
        case Color.Red:
            return 'red';
        case Color.Green:
            return 'green';
    }
    return assertUnreachable(c);
}

이 시점에서, 다음의 에러가 표시됩니다.

return assertUnreachable(c);
       ~~~~~~~~~~~~~~~~~~~~~
       Type "Color.Blue" is not assignable to type "never"

에러 메세지는, 모든 스위치에 포함시키는 것을 잊은 케이스를 나타내고 있습니다.값을 여러 개 생략하면 에 대한 오류가 표시됩니다. Color.Blue | Color.Yellow.

하세요, 만약 하고 있다면.strictNullChecks게 할 거예요.return assertUnreachable 콜(콜, 콜(콜, 콜)

원한다면 좀 더 멋있게 살 수 있어.예를 들어 식별된 결합을 사용하는 경우 디버깅을 위해 어설션 함수의 식별 속성을 복구하는 것이 유용할 수 있습니다.다음과 같습니다.

// Discriminated union using string literals
interface Dog {
    species: "canine";
    woof: string;
}
interface Cat {
    species: "feline";
    meow: string;
}
interface Fish {
    species: "pisces";
    meow: string;
}
type Pet = Dog | Cat | Fish;

// Externally-visible signature
function throwBadPet(p: never): never;
// Implementation signature
function throwBadPet(p: Pet) {
    throw new Error('Unknown pet kind: ' + p.species);
}

function meetPet(p: Pet) {
    switch(p.species) {
        case "canine":
            console.log("Who's a good boy? " + p.woof);
            break;
        case "feline":
            console.log("Pretty kitty: " + p.meow);
            break;
        default:
            // Argument of type 'Fish' not assignable to 'never'
            throwBadPet(p);
    }
}

이는 컴파일 시 안전성이 확보되어 예상한 모든 사례를 확실하게 처리할 수 있기 때문에 좋은 패턴입니다., 진정한 를 들면, 「」를 하고 있는 )을 취득할 수 .species을 사용법

안 돼요.never '이라는 에다가 덧붙입니다.switch.

한다면

  • 의 ★★★★★★★★★★★★★★★★★.switch는 각 됩니다.
  • 이 경우,strictNullChecks compilation on ( typescript flag on ) 。
  • 함수에 지정된 반환 유형이 있습니다.
  • .undefined ★★★★★★★★★★★★★★★★★」void

가 나면 .switch아무것도 반환되지 않는 경우가 있기 때문에 스테이트먼트는 중단되지 않습니다.

예를 들어 다음과 같은 경우

function getColorName(c: Color): string {
    switch(c) {
        case Color.Red:
            return 'red';
        case Color.Green:
            return 'green';
        // Forgot about Blue
    }
}

다음과 같은 컴파일 오류가 발생합니다.

끝에는 반환 유형이 .undefined.

Ryan의 답변에 기초하여, 저는 여기서 추가 기능이 필요하지 않다는 것을 알게 되었습니다.델은 다음과 같은 작업을 직접 수행할 수 있습니다.

function getColorName(c: Color): string {
  switch (c) {
    case Color.Red:
      return "red";
    case Color.Green:
      return "green";
    // Forgot about Blue
    default:
      const exhaustiveCheck: never = c;
      throw new Error(`Unhandled color case: ${exhaustiveCheck}`);
  }
}

TS Playground에서 동작하고 있는 것을 보실 수 있습니다.

편집: "미사용 변수" 린터 메시지를 피하기 위한 제안 포함.

typescript-eslint에는, 「유니온 타입을 사용한 스위치에서의 일관성 체크 인」규칙이 있습니다.
@typescript-eslint/switch-comparency-check

하려면 , 「 」, 「 」의 .package.json타이프 스크립트React 17 서 react react react :

"eslintConfig": {
    "rules": {
        "@typescript-eslint/switch-exhaustiveness-check": "warn"
    },
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "project": "./tsconfig.json"
    }
},

여기에 이미지 설명 입력

솔루션

에러 클래스를 정의합니다.

export class UnreachableCaseError extends Error {
  constructor(val: never) {
    super(`Unreachable case: ${JSON.stringify(val)}`);
  }
}

디폴트의 경우는, 다음의 에러를 슬로우 합니다.

enum Color {
    Red,
    Green,
    Blue
}

function getColorName(c: Color): string {
  switch(c) {
      case Color.Red:
          return 'red, red wine';
      case Color.Green:
          return 'greenday';
      case Color.Blue:
          return "Im blue, daba dee daba";
      default:
          // Argument of type 'c' not assignable to 'never'
          throw new UnreachableCaseError(c);
  }
}

라이언이 것 요.throw을 사용법

힌트

ts-essentials 라이브러리의 클래스 UnreachableCaseError는 이 사용 사례에 정확히 해당됩니다.

실행 시 고려 사항

이 타이프스크립트 코드는 javascript로 변환됩니다.따라서 모든 타이프스크립트 형식 검사는 컴파일 시에만 작동하며 런타임에는 존재하지 않습니다. 즉, 변수가 실행 중이라는 보장은 없습니다.c 타입이다Color.
이것은 다른 언어와는 다릅니다.예를 들어 Java는 실행 시 유형을 체크하고 잘못된 유형의 인수로 함수를 호출하려고 하면 의미 있는 오류를 발생시킵니다.- javascript는 그렇지 않습니다.

에 in문,, 요 in in in in in the the the in in the the 에서 의미있는 합니다.default케이스: Stackblitz: 슬로우 의미 있는 오류

하지 않으면 는 이렇게 .getColorName()으로 반환하다undefined(예상치 않은 인수로 호출된 경우):Stackblitz: 임의 반환

에서는 직접 했습니다.any를 참조해 주세요.실제 프로젝트에서는 이 문제가 발생하지 않기를 바라지만 실행 시 잘못된 유형의 변수를 얻을 수 있는 방법은 여러 가지가 있습니다.
여기 제가 이미 봤던 몇 가지 실수가 있습니다(저도 몇 가지 실수를 저질렀습니다).

  • 형식을 - 은 타입으로 되어 있습니다:타입으로 되어 .any
    Stackblitz "Stackblitz"
  • 암묵적 any가 허용됩니다.
  • 외부 값이 사용되고 검증되지 않음(예를 들어 서버로부터의 http-response가 인터페이스에 캐스트됨)
  • 이전 버전의 앱이 작성한 로컬 스토리지에서 값을 읽습니다(이러한 값이 변경되었기 때문에 새 로직은 이전 값을 인식하지 못합니다).
  • 타입 세이프가 아닌(또는 단순히 버그가 있는) 서드파티제의 lib를 사용하고 있습니다.

따라서 게으름 피우지 말고 이 추가 디폴트 케이스를 작성하십시오.이것에 의해, 많은 골칫거리가 해소됩니다.

라이언의 대답에 대한 좋은 반전으로, 당신은 대신할 수 있다.never임의의 문자열을 사용하여 오류 메시지를 보다 쉽게 사용할 수 있습니다.

function assertUnreachable(x: 'error: Did you forget to handle this type?'): never {
    throw new Error("Didn't expect to get here");
}

이제 다음을 얻을 수 있습니다.

return assertUnreachable(c);
       ~~~~~~~~~~~~~~~~~~~~~
       Type "Color.Blue" is not assignable to type "error: Did you forget to handle this type?"

것은 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 맞다.'never임의의 문자열을 포함한 모든 것에 할당할 수 있습니다.

Ryan과 Carlos의 답변을 바탕으로 익명의 방법을 사용하여 별도의 명명된 함수를 만들 필요가 없습니다.

function getColorName(c: Color): string {
  switch (c) {
    case Color.Red:
      return "red";
    case Color.Green:
      return "green";
    // Forgot about Blue
    default:
      ((x: never) => {
        throw new Error(`${x} was unhandled!`);
      })(c);
  }
}

스위치가 완전하지 않으면 컴파일 시간 오류가 발생합니다.

Typescript 또는 linter 경고를 방지하려면:

    default:
        ((_: never): void => {})(c);

컨텍스트:

function getColorName(c: Color): string {
    switch(c) {
        case Color.Red:
            return 'red';
        case Color.Green:
            return 'green';
        default:
            ((_: never): void => {})(c);
    }
}

이 솔루션과 다른 솔루션의 차이점은

  • 참조되지 않은 명명된 변수는 없습니다.
  • 가 Typescript로 강제 .never 실행하다

누락된 케이스를 찾는 가장 쉬운 방법은 TypeScript에서 암묵적인 반환이 없는지 확인하는 것입니다.셋팅 완료noImplicitReturns로로 합니다.true compilerOptionstsconfig.jsonfilename을 클릭합니다.

다음에는 ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴthrow new ErrorTypeScript 컴파일러가 에러를 발생시키지 않기 때문에(코드가 이미 에러를 발생시키고 있기 때문에):

enum Color {
  Red,
  Green,
  Blue
}

function getColorName(c: Color): string {
  switch (c) {
    case Color.Red:
      return 'red';
    case Color.Green:
      return 'green';
  }
}

코드를 반환이 됩니다).undefined타이프 스크립트

TS2366: 기능에 종료 리턴 문이 없으며 리턴 유형에 '정의되지 않음'이 포함되어 있지 않습니다.

또, 그것을 실증하는 비디오도 만들었습니다.https://www.youtube.com/watch?v=8N_P-l5Kukk

또한 함수의 반환 유형을 좁힐 것을 제안합니다.할 수 .string 정의된 .

function getColorName(c: Color): 'red' | 'blue'

반환 유형을 좁히면 일부 IDE(VS Code 및 Web Storm 등)에서 필드를 사용하지 않을 때 표시되므로 분실 사례를 찾는 데 도움이 됩니다.

매핑 타입은 다음과 같이 사용할 수 있습니다.

enum Color {
  Red,
  Green,
  Blue,
}

type ColorMapper = {
  [Property in Color]: string
}

const colorMap: ColorMapper = {
  [Color.Red]: "red",
  [Color.Green]: "green",
  [Color.Blue]: "blue",
}

function getColorName(c: Color): string {
  return colorMap[c];
} 

한 후Color ColorMapper요구 사항들.

몇 개의 문자열을 열거값으로 반환해야 하는 매우 단순한 경우에서는 스위치를 사용하는 대신 결과 사전을 저장하기 위해 몇 개의 상수를 사용하는 것이 더 쉽습니다(IMHO).예를 들어 다음과 같습니다.

enum Color {
    Red,
    Green,
    Blue
}

function getColorName(c: Color): string {
  const colorNames: Record<Color, string> = {
    [Color.Red]: `I'm red`,
    [Color.Green]: `I'm green`,
    [Color.Blue]: `I'm blue, dabudi dabudai`,   
  }

  return colorNames[c] || ''
}

따라서 여기서 모든 열거값을 지속적으로 언급해야 합니다.그렇지 않으면 예를 들어 파란색이 없는 경우 다음과 같은 오류가 발생합니다.

TS2741: 유형 '{ [컬러]에 속성 '파란색'이 없습니다.빨간색 : 문자열; [색상]녹색]: 문자열;'이지만 'Record' 형식에는 필수 항목입니다.

그러나 종종 그렇지 않고 Ryan Cavanaugh가 제안한 것처럼 오류를 던지는 것이 더 낫다.

또, 이것 역시 효과가 없는 것을 알고, 조금 당황했습니다.

function getColorName(c: Color): string {
    switch(c) {
        case Color.Red:
            return 'red';
        case Color.Green:
            return 'green';
    }
    return '' as never // I had some hope that it rises a type error, but it doesn't :)
}

를 만들 때, 커스텀 함수를 합니다.switch★★★★★★ 。

export function exhaustSwitch<T extends string, TRet>(
  value: T,
  map: { [x in T]: () => TRet }
): TRet {
  return map[value]();
}

사용 예

type MyEnum = 'a' | 'b' | 'c';

const v = 'a' as MyEnum;

exhaustSwitch(v, {
  a: () => 1,
  b: () => 1,
  c: () => 1,
});

에 " " " 를 했을 경우d로로 합니다.MyEnumProperty 'd' is missing in type ...

switch...case의 일반적인 사용 사례인 태그 부착 유니언 타입 전용의 유용한 변종을 추가하고 싶습니다.이 솔루션은 다음을 실현합니다.

  • 전치 시 타이프 체크
  • 런타임 체크도 가능합니다.왜냐하면 타이프 스크립트는 버그가 없는 것을 보증하지 않기 때문입니다.데이터가 어디서 왔는지 누가 알겠습니까?
switch(payment.kind) {

        case 'cash':
            return reduceⵧprocessꘌcash(state, action)

        default:
            // @ts-expect-error TS2339
            throw new Error(`reduce_action() unrecognized type "${payment?.kind}!`)
    }

는 ' 유형을하지 않고 절대' 탐지는 '절대' 기본 유형을 참조하지 않고 무료로 제공됩니다.에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 나기 때문에 에러가 .// @ts-expect-error우리 코드가 틀리면 실패할 수 있습니다.에러 ID가 곧 지원될 경우를 대비해서 언급하고 있습니다.

이것은 키워드가 있는 TypeScript 4.9에서 훨씬 더 쉬워졌습니다.

enum Color {
    Red,
    Green,
    Blue
}

function getColorName(c: Color): string {
  switch(c) {
      case Color.Red:
          return 'Red';
      case Color.Green:
          return 'Green';
      case Color.Blue:
          return 'Blue';
      default:
          c satisfies never;
  }
}

의 수표가하다면, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」c 이 되어 있어야 한다never에게 '라고 합니다.satisfies「」(「」)를 것)c 할 수 한다never에러에 대응합니다.나중에 새로운 케이스를 열거에 추가하면 완전한 컴파일 시간 오류가 발생합니다.

★★★★★★★★★★★★★★★★★★★★.default다음 중 하나:

default:
    c;

만 합니다.이 표현식에서는c코드에 않을 당신이 코드에 영향을 준다면c예를 들어, 클래스의 getter가 평가되어 부작용이 발생할 수 있습니다(현재 받아들여지고 있는 답변).

언급URL : https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript

반응형