Language-tools: TypeScriptは、変数をチェックする条件内で変数をnullの可能性があると誤って見なします

作成日 2020年10月18日  ·  20コメント  ·  ソース: sveltejs/language-tools

バグを説明する
ネストされた条件チェックに対して、TypeScriptエラー「オブジェクトがnullである可能性があります」が表示されます。

再現するには
簡単なテストケース:

<script lang="typescript">
    interface TestObject {
        author?: string;
    }
    export let test: TestObject | null;
</script>

{#if test}
    <div>
        {#if test.author}
            <div>Author: {test.author}</div>
        {/if}
    </div>
{/if}

TypeScriptは、行{#if test.author} 、特にその行内のtestではなく、エラーObject is possibly nullをスローします。

予想される行動
エラーをスローしないことが期待されます。

システム(以下の情報を入力してください):

  • OS:OSX 10.15.7
  • IDE:VSCode
  • プラグイン/パッケージ:「SvelteforVSCode」

追加のコンテキスト
ネストされた条件ステートメントが削除され、TestObjectインターフェイスでauthor?author置き換えられた場合(必須にするため)、TypeScriptが{test.author}に対して同じエラーをスローするのは論理的です。 、しかしそうではありません。 したがって、エラーはネストされた条件ステートメントによってトリガーされるように見えますが、それがないと、TypeScriptはtestがnullではないことを認識します。

Fixed bug

最も参考になるコメント

小さな更新/要約:

  • コードが#await#eachlet:Xいずれかの構造内に入るとすぐに、制御フローがリセットされます。 これは、コードを変換する方法によるものです。 私たちはこの問題を忘れていませんが、これを良い方法で解決する方法をまだ探しています。
  • ネストされたif条件は、最初の箇条書きで述べたものの1つによって中断されない限り、期待どおりに機能するはずです。

全てのコメント20件

それに対処するために何ができるかわからない。 svelte2tsxはそれをこれに変換します:

() => (<>

{() => {if (test){<>
    <div>
        {() => {if (test.author){<>
            <div>Author: {test.author}</div>
        </>}}}
    </div>
</>}}}</>);

これは、制御フロータイプの分析が有効にならないように関数にラップされているためです。 また、依存関係が変更されると式が再実行されるため、関数を削除したり、すぐに呼び出された関数にラップしたりすることはできません。制御フロータイプの一部を無効にする必要があります。

これを三元化できますか? elseブロックがない場合は、elseの場合に空の文字列を使用します。
しかし、より大きな問題は、どの変換でもそのような関数を使用することが許可されていないことかもしれません-それが今の場合かどうかはわかりません

多分許可してください! 構文? {#if test!.author} 。 これは有効なTypeScript構文ですが、現在、テンプレートでは使用できません。

現在、マークアップでtypescriptを使用することはできません。 マークアップを前処理したり、コンパイラにタイプスクリプト構文を解析させたりする方法はありません。 したがって、null以外のアサーションは不可能です。
実行可能性の購入を試したことがない場合は、上位レベルの条件をコピーして、ネストされた条件の前に追加できます。

 {() => {if (test && test.author){<>
            <div>Author: {test.author}</div>
        </>}}}

一時的な回避策:

<script lang="typescript">
    interface TestObject {
        author?: string;
    }
    export let test: TestObject | null;

    let requitedTest: Required<TestObject>;
    $: {
        requiredTest = test as unknown as Required<TestObject>;
    }
</script>

{#if test}
    <div>
        {#if requiredTest.author}
            <div>Author: {requiredTest.author}</div>
        {/if}
    </div>
{/if}

変数をコピーし、異なるタイプを割り当て、ネストされた状態で使用します。 それは醜く、注意深く使用する必要がありますが、警告を取り除きます。

今のところ、より実用的な回避策は次のようになります。

{#if test}
    <div>
        {#if test?.author}
            <div>Author: {test.author}</div>
        {/if}
    </div>
{/if}

特に、同じオブジェクト内をドリルするだけでなく、別々の条件を作成する場合は、非常に面倒です。

{#if bigSwitch}
    <div>
        {#if smallSwitch}
            <MyComponent {bigSwitch} />
        {/if}
    </div>
{/if}

それはうまくいきません。 TypeScriptはテンプレートでは許可されていません。

ここで動作するようです。 test && test.authorそれでは。

オプションのチェーンを意味する場合、それは単なるtypescript機能ではなく、新しいjavascript機能です。 3.24.0以降はsvelteで利用可能です

ありがとう! null以外のステートメントfoo!似たTS機能だといつも思っていました。 それはそれをはるかに簡単にします。

常に新しいことを学ぶ:)

実行可能性の購入を試したことがない場合は、上位レベルの条件をコピーして、ネストされた条件の前に追加できます。

 {() => {if (test && test.author){<>
          <div>Author: {test.author}</div>
      </>}}}

私はその方向でいくつかのテストを行いましたが、これはすべての場合に機能するとは限りません。 #ifエラーは無音になる場合がありますが、その中で#eachまたは#awaitブロックを使用するとすぐにエラーが再表示されます。 変数がconst場合、これらのエラーは表示されません。これは、変数を再割り当てできないため、TS制御フローが条件がまだ真でなければならないことを認識しているためです。 したがって、これを解決する最善の方法は、すべてをconst変数に変換するか、すべての変数をテンプレートの他の一時的な変数に再割り当てすることです。 ただし、ホバー情報、名前の変更、宣言への移動などには独自の問題が発生します。おそらく、カンマ式を使用して{#if foo}{#if (foo, generatedConstFoo)}変換することで回避できます。 結論:これにはかなりのハッカーが必要になります。

たぶん「上位レベルのifブロックを追加する」-結局のところ正しい。 各/待機ブロック内で条件がまだ真であると想定するのは安全ではありません。 たとえば、これはランタイムエラー( toUpperCase is not a function )をスローします:

<script>
    let name = 'name';
    function setNameToNumber(){
        name = 1
        return ['a']
    }
</script>

{#if name}
    <h1>Hello {name.toUpperCase()}!</h1>
    {#each setNameToNumber() as item}
        item
    {/each}
{/if}

await-blockについても同じことが言えます。

小さな更新/要約:

  • コードが#await#eachlet:Xいずれかの構造内に入るとすぐに、制御フローがリセットされます。 これは、コードを変換する方法によるものです。 私たちはこの問題を忘れていませんが、これを良い方法で解決する方法をまだ探しています。
  • ネストされたif条件は、最初の箇条書きで述べたものの1つによって中断されない限り、期待どおりに機能するはずです。

現在、マークアップでtypescriptを使用することはできません。 マークアップを前処理したり、コンパイラにタイプスクリプト構文を解析させたりする方法はありません。 したがって、null以外のアサーションは不可能です。
実行可能性の購入を試したことがない場合は、上位レベルの条件をコピーして、ネストされた条件の前に追加できます。

 {() => {if (test && test.author){<>
          <div>Author: {test.author}</div>
      </>}}}

#493が$store変数を宣言するように修正されたので、このアイデアを再検討できます。 私の考えは、次のようなすべてのif条件を前に付けることです(例として#each ):

{#if somecondition && anothercondition}
   {#each ..}
     ..
   {/each}
{/if}

でる

<>{__sveltets_each((true, items), (item) => (somecondition && anothercondition) && <>
    ...
</>)}</>

トリッキーな部分は、elseif / elseとネストロジックを正しくすることです。

これは、VSCodeバージョン104.6.3 / svelte-checkバージョン1.2.4で修正する必要があります。 それが今あなたのために働くかどうか私に知らせてください。

問題が見つかりました-これはテンプレート内では正しく機能しますが、イベントリスナーでは機能しません。 新しい問題を開く必要があるかどうかはわかりませんが、ここに再現があります。

<script lang="ts">
  let value: string | null = null;

  function acceptsString(value: string) {
    console.log(value);
  }
</script>

{#if value}
  <!-- Argument of type 'string | null' is not assignable to parameter of type 'string'.
  Type 'null' is not assignable to type 'string'. -->
  <button on:click={() => acceptsString(value)} />
{/if}

問題が見つかりました-これはテンプレート内では正しく機能しますが、イベントリスナーでは機能しません。 新しい問題を開く必要があるかどうかはわかりませんが、ここに再現があります。

<script lang="ts">
  let value: string | null = null;

  function acceptsString(value: string) {
    console.log(value);
  }
</script>

{#if value}
  <!-- Argument of type 'string | null' is not assignable to parameter of type 'string'.
  Type 'null' is not assignable to type 'string'. -->
  <button on:click={() => acceptsString(value)} />
{/if}

ただし、これは通常のTSの動作です。 値は、値がnullに戻る可能性がある将来いつでも呼び出すことができる関数で参照されます。

どちらも有効なビューです。 「 valueは定数ではないため、変更される可能性があります」と言うこともできますが、「 valuenull場合、そのクリックハンドラーに到達することはありません」と言うこともできます。

これについては別の問題を開いて、個別に説明できるようにします。

TS自身の古い選択について話し合うのは時間の価値がないと思います:p
たとえば、親または親のレベルでアウトトランジションがある場合、ボタンはvalue === nullのままになりませんか?

厳密に言えば、各ループ内の他の変数を変更したり、待機したりできるため、これらのリラックスした変更の前に制御フローは正しいものでした。

<script lang="ts">
  let foo: string | null = 'bar';
  function getItems() {
     foo = null;
     return [''];
  }
</script>

{#if foo}
  {#each getItems() as item}
    {foo.toUpperCase()} <!-- boom -->
  {/each}
{/if}

..しかし、誰がこれをしますか? ところで、TSでも同じランタイムエラーを作成できます。 したがって、「これは100%正しい」というよりも、DXのトレードオフになります。

ただし、ここでこれについてさらに説明する前に、この説明を#876に行ってください😄

このページは役に立ちましたか?
0 / 5 - 0 評価