Почему это дает предупреждение Typescript? - PullRequest
0 голосов
/ 23 марта 2020

Почему второй вызов needsString(var1) в этом компоненте .tsx выдает предупреждение, а не первый или третий вызов? И если я изменю код для использования var2 вместо var1 (который, я думаю, имеет точно такие же типы), я не получу предупреждение. Что дает?

import { useParams } from "react-router-dom";

const DeleteTeams2 = ({ var2 } : { var2: string | undefined})=> {
    // use var here so that var1 and var2 get exactly the same types
    var { var1 } = useParams();

    function needsString(str : string) : string {
        return "hello";
    }

    // I can tell typescript that var1
    // definitely is non-null with an if statement
    if (! var1)
        throw new Error("bad, bad varN");

    // This call is warning-free
    needsString(var1);

    // But JSX doesn't know that var1 is non-null for some reason...
    return (
        <div
            onClick={() => {
                // I get a warning from this!
                needsString(var1);
            }}
        >
            { needsString(var1) }
        </div>
    )
}

Когда я перемещаю указатель мыши над определениями var1 и var2, кажется, они оба имеют одинаковый тип: var varN: string | undefined.

Из-за в операторе if я ожидаю, что var1 будет вести себя как string после оператора if. И действительно, это действительно для var2, но не для var1.

Но я получаю предупреждение при первом вызове needsString(var1):

TypeScript error in /path/to/foobar.tsx(146,29):
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
  Type 'undefined' is not assignable to type 'string'.  TS2345

    144 |             onClick={() => {
    145 |                 // I get a warning from this!
  > 146 |                 needsString(var1);
        |                             ^
    147 |             }}
    148 |         >
    149 |             { needsString(var1) }

Редактировать: Удалена ссылка на Оператор ненулевого подтверждения , поскольку я не использовал оператор ненулевого подтверждения, как отмечалось в нескольких комментариях.

1 Ответ

2 голосов
/ 23 марта 2020

Проблема в том, что код здесь:

onClick={() => {
  // I get a warning from this!
  needsString(var1);
}}

не будет выполнен одновременно с этим кодом:

if (! var1)
  throw new Error("bad, bad varN");

// This call is warning-free
needsString(var1);

Более того, var1 является изменяемой ссылкой так что с точки зрения TypeScript может измениться между проверкой, если она пуста, на самом деле вы ее используете. Например, рассмотрим этот пример кода:

//it's populated
var myVar = "hello";

//verify it's populated
if(!myVar) throw Error("WRONG!");

//absolutely positively `myVar` is populated

//set this to execute later
setTimeout(() => console.log("myVar", myVar));

//change it
myVar = undefined;

Таким образом, TypeScript может только определить, что var1 может быть переназначен. Функция useParams() возвращает объект, типизированный как

{ [K in keyof Params]?: string | undefined; }

, что означает, что возможный тип для var1 равен string | undefined. Если значение изменяется (TypeScript не может определить, делает это или нет), то он может видеть, что undefined недопустимо.

Использовать const

Вы можете просто объявить его как const { var1 } ( Playground Link ), что делает его тип string только потому, что он гарантирует компилятору, что вы не измените его между объявлением и использованием.

Утверждение типа

В качестве альтернативы можно явно указать тип string: var { var1 } = useParams() as { var1: string }; Playground Link . Компилятор согласился бы с тем, что возможно вы переназначили его, но он по крайней мере знал бы, что единственная возможная вещь, на которую вы бы изменили переменную, - это другая строка. Который удовлетворяет типу, используемому позже, и таким образом будет работать. Однако это требует утверждения типа и, как всегда, полагается на то, что вы уверены , что это так, иначе вы можете получить ошибку времени выполнения, которую не увидит компилятор TypeScript (потому что вы «ослепили») это, намеренно)

const только после проверки

Если вам неудобно устанавливать постоянную var1 по какой-либо причине - либо вы ее измените, либо что-то еще, тогда вы можете просто выполните обычную проверку для var1 и затем установите новую константу:

if (! var1)
  throw new Error("bad, bad varN");

const checkedVar1 = var1;

/* ... */

onClick={() => {
  // I get a warning from this!
  needsString(checkedVar1);
}}

Playground Link

Это все еще работает и будет удовлетворять компилятору TypeScript, который checkedVar1 (Полагаю, вы бы назвали это чем-то более разумным) не меняется между определенным назначением чего-либо, что определенно является строкой, и фактическим использованием.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...