93750
  
  
次のコードをTypeScriptPlaygroundで動かしてみたところ、  
testFunc(a);  
のところで、aがnullの可能性があるとして、型エラーになっていました。  
この型エラーを解消するために、isValidにユーザー定義型ガード`a is
string`のようなことを記載したいのですがどのように記載するとよいのかがわかりません。

ご存知の方、おられましたら、教えてください。

a に string | null の型ではなく、stringを割り当てたらそういうことは起きないですが、  
実際には固定値ではないコードなので、string | null で変数が割り当たる部分は  
そのままにしておきたいです。

よろしくお願いします。

[code]

    const isValid = ({a, b}: {a: string | null, b: string | null}): boolean => {
      if (!a) {
        return false;
      }
      if (!b) {
        return false;
      }
      return true;
    }
    
    const testFunc = (args: string) => {
      console.log(args);
    }
    
    const main = () => {
      const a: string | null = null;
      const b: string | null = "B";
    
      if (isValid({a, b})) {
        testFunc(a);
      } else {
        console.log("test");
      }
    
    }
    main();
    
    
[/code]


CC BY-SA 4.0
2023-02-06T08:40:45.207
93729
2023-02-07T12:02:03.867
21047
question
typescript
TypeScriptの2つの値に対するユーザー定義型ガードについて


TypeScript のバージョンは、現時点の最新版である v4.9 という前提で解答します。

まず、第一に ユーザー定義の型ガード関数 (User-Defined Type Guard) は、戻り値の型が `foo is type` のような
`Type predicate` でなくてはなりません。  
また、述部で言及できるのは一つの引数についてだけとなり、 分割代入引数は使えなさそうです。  
<https://www.typescriptlang.org/docs/handbook/2/narrowing.html?#using-type-
predicates>

次に、 if 文で type predicate を使う場合、そのスコープ内の該当部分までのコードフローの中で取りうる、最も厳しい内容が使われます。  
つまり、いくら型注釈していても、 `const a: string | null = null;` と書けば、 `a` の型は `null` なのです。  
サンプルを書く場合は、（書き方は色々ありますが）そのスコープ単体では実行時まで値が決定しないように書くと良いでしょう。

また、 `{ a, b }` はあくまで `{ a: a, b: b }` の省略表記であり、このオブジェクトの `a` プロパティと `a`
変数は別物であるため、現状 `a` プロパティの型ガードの結果は `a` 変数に伝搬しなさそうです。

* * *

といったあたりを総合すると、提示されたコードの構造を保ったまま直すなら、以下となるでしょうか。

[code]

    const isValid = (arg: any): arg is {a: string, b: string } => {
      if (!arg.a) {
        return false;
      }
      if (!arg.b) {
        return false;
      }
      return true;
    }
    
    const testFunc = (args: string) => {
      console.log(args);
    }
    
    const main = () => {
      const a: string | null = ["A", null][Math.floor(Math.random() * 2)];
      const b: string | null = ["B", null][Math.floor(Math.random() * 2)];
      const tmp = { a, b };
    
      if (isValid(tmp)) {
        testFunc(tmp.a);
      } else {
        console.log("test");
      }
    
    }
    main();
    
[/code]

ただ、これだと変数 `a`, `b` の書き方がクドいので、 `main` 関数周りは、以下のように書くほうがオススメです。

[code]

    const main = (a: string | null, b: string | null) => {
      const tmp = { a, b };
    
      if (isValid(tmp)) {
        testFunc(tmp.a);
      } else {
        console.log("test");
      }
    
    }
    main(null, "B");
    
[/code]


CC BY-SA 4.0
2023-02-07T12:02:03.867
93750
2023-02-07T12:02:03.867
8237
93729
answer