조건부 유형에 값을 어떻게 할당합니까?

Nov 18 2020

나는 첫 번째 매개 변수가되도록 함수를 작성하기 위해 노력하고있어 boolean,이 인수가 있는지 여부에 따라 true또는 false, 두 번째 인수 중 하나 받아들이는 함수 string또는 string[].

내 시도는 다음과 같습니다.

type P<B extends boolean> = B extends true ? string[] : string

function callback<B extends boolean>(b: B, t: (f: P<B>) => void) {
    const file = 'file'
    if (b) {
        t([file]) // <-- Error: Argument of type 'string' is not assignable to parameter of type 'P<B>'.
    } else {
        t(file)   // <-- Error: Argument of type 'string[]' is not assignable to parameter of type 'P<B>'.
    }
    
}

callback(false, (f: string) => {})  // <-- No problem, resolves the correct argument type
callback(true, (f: string[]) => {}) // <-- No problem, resolves the correct argument type

이것은 함수가 호출 될 때 올바른 인수 유형을 해결하기 위해 작동합니다. 그러나 함수 내부에서 TS 컴파일러는 조건부 유형을 string또는로 확인할 수 없다는 오류를 표시합니다 string[]. 이를 수행하는 올바른 방법은 무엇입니까?

놀이터 링크 .

답변

3 crashmstr Nov 18 2020 at 13:22

다음은 두 매개 변수가있는 객체를 전달하여 수행하는 한 가지 방법입니다. true하나의 서명 또는 false다른 서명 이있는 형식을 사용 하여 컴파일러는 b속성 을 테스트하여 개체를 구별 할 수 있습니다 .

type f = { b: false, t: (f: string) => void };
type t = { b: true, t: (f: string[]) => void };
type fOrT = f | t;

function callback(x: fOrT) {
    const file = 'file'
    if (x.b) {
        x.t([file])
    } else {
        x.t(file)
    }
    
}

callback({ b: false, t: (f: string) => {} })
callback({ b: true, t: (f: string[]) => {} })

TypeScript 플레이 그라운드

2 KrzysztofKrzeszewski Nov 18 2020 at 13:30

예를 들어 사용자 정의 입력을 작성하여 다른 변수를 기반으로 유형을 좁힐 수 있습니다.

function instanceOfMyTYpe(textArray: string[] | string, flag: boolean): textArray is string {
    return flag;
}

let file = "test" as string | string[];
file.toUpperCase(); // doesn't work. Property 'toUpperCase' does not exist on type 'string | string[]'.
if (instanceOfMyTYpe(file, true)) file.toUpperCase(); // but this works
else file.forEach((text)=>text.toUpperCase()); // and so does this

한 변수를 다른 변수와 연결하지 않으므로 잘못된 함수 호출이 계속 발생할 수 있습니다. 예를 들어 배열과 참 플래그를 사용하여 함수를 호출하면주의하지 않아도 프로그램이 배열이 실제로 문자열이라고 생각하게됩니다.

1 AlexChashin Nov 18 2020 at 13:19

이와 같은 일반 유형 인수를 사용하는 것은 목에 정말 고통 스럽습니다. 이것이 정확히 어떻게 작동하는지 확실하지 않지만 typescript가 유형을 좁힐 수는 거의 없습니다. 구성 t내부로 마우스를 가져 if가면 유형 (f: P<B>) => void(f: string) => void있거나 없는 것을 볼 수 있습니다 (f: string[]) => void. 또는 : 다른 변수에 따라 한 변수의 유형을 좁힐 수 없었습니다. 나는 이것이 한계라고 생각하고 지금은 이것을 고칠 방법을 생각할 수 없습니다. 내가 틀렸을 수도 있지만, 이전에 좀 더 복잡한 맥락에서이 상황에 직면했고 작동하도록 기능의 디자인을 변경해야했습니다.

난 그냥 할 수있는이 경우에 생각 t([file] as any)하고 t(file as any), 마지막으로, 이러한 유형의 포인트는 올바른 방법으로 호출하는 기능을 강제하는 것입니다. 올바르게 호출되면 내부에서 무엇을해야하는지 알고 any있습니다. 여기에 몇 개의 s를 추가 할 가치가 있다고 생각합니다 .

또한 오버로드를 사용하여 제네릭을 제거 할 수 있지만 문제가 해결되지는 않습니다.

function callback(b: true, t: (f: string[]) => void): void
function callback(b: false, t: (f: string) => void): void
function callback(b: boolean, t: ((f: string) => void)) | ((f: string[]) => void)) {
  //
}

실제로 anys가 필요하지 않은 한 가지 해결책은 객체를 사용하는 것이지만 응용 프로그램에서 다른 논리를 변경해야 할 수도 있으므로 이상적이지 않습니다.

type Options = {
  b: true
  t: (f: string[]) => void
} | {
  b: false
  t: (f: string) => void
}

function callback(options: Options) {
  const file = 'file'
  if(options.b) options.t([file])
  else options.t(file)