Эта проблема

Ниже приведен фрагмент кода React + TypeScript, который я только что написал (упрощенный, чтобы мы могли сосредоточиться на вызываемой им ошибке):

const Foo = ({className}) => 
  <Popper className={className + " someCustomClassName"} />

Это вызывает следующую ошибку TypeScript:

TS7031: Binding element 'className' implicitly has 'any' type.

Я понимаю, что это означает, что я не объявлял явно тип для переменной className, поэтому она «неявно» имеет тип any, но я не понимаю, почему это проблема. Это кажется нелогичным - не должны any типы допускать… любые типы ??

В кроличью нору

Немного поискав в Интернете позже, я нашел список кодов ошибок для TypeScript в репозитории: https://github.com/Microsoft/TypeScript/blob/2.1/src/compiler/diagnosticMessages.json#L3072

Это показывает источник сообщения об ошибке:

"Binding element '{0}' implicitly has an '{1}' type.": {
  "category": "Error",        
  "code": 7031    
},

… Не очень полезно, но всегда приятно находить что-то в источнике, а не тупые сообщения в блогах (например, этот)…

Итак, в чем проблема, компилятор TypeScript? Я знаю, я могу просто объявить тип для моей переменной className и двигаться дальше ... но я хочу понять, почему.

Исправление, но я все еще ничего не знаю

Так, например, это «решит» проблему:

const Foo = ({className}:{[key:string]:any}) => 
  <div className={className + " someCustomClassName"} />

В этом фрагменте я явно объявил все ключи в объекте первого аргумента как string и все значения этого объекта как any. Итак, единственное различие, которое я могу сказать, заключается в том, что я явно объявляю any, а не неявно. Я до сих пор не знаю, почему это лучше, потому что TypeScript способен выводить типы, то есть мне не нужно писать const foo: number = 5, я могу просто писать const foo = 5, а TypeScript выводит (выводить как вывод из фактов или предпосылки »), что тип переменной foo number. В этом простом foo примере TypeScript использует самую простую версию вывода типов:

Этот вид вывода имеет место при инициализации переменных и членов, установке значений параметров по умолчанию и определении типов возвращаемых функций.

Когда переменная инициализируется, предполагается, что тип переменной foo будет number. Есть два других способа определения типов: наиболее распространенный тип и контекстный тип, но я не думаю, что они имеют отношение к теме этой публикации.

Вниз по кроличьей норе, Глава 2

Может быть полезно вкратце подумать об основной структуре и синтаксисе моего исходного фрагмента кода. Давайте посмотрим на это еще раз:

const Foo = ({className}) => 
  <Popper className={className + " someCustomClassName"} />

Я использую деструктуризацию объекта (назначение деструктуризации AKA) для извлечения className из объекта первого аргумента. Более подробный способ написать это:

const Foo = (obj) => {
  const className = obj.className
  return <Popper className={className + " someCustomClassName"} />
}

Деструктуризация - это функция JavaScript (ECMAScript 2015), поэтому она также доступна в TypeScript.

В другом измерении…

Один из способов использования вывода типа здесь - установить значение по умолчанию для параметра className:

const Foo = ({className = 'someDefaultValue'}) => 
  <Popper className={className + " someCustomClassName"} />

При этом используется вывод типа посредством улучшенной проверки литерала объекта деструктуризации (также здесь): Свойства со значениями по умолчанию в шаблоне привязки объекта становятся необязательными в литерале объекта. Итак, мы явно устанавливаем значение по умолчанию, равное string, а параметр className неявно определяется как string. К сожалению, в моем случае я не хочу определять значение по умолчанию, поэтому для меня это не сработает.

TL; DR; Нет Неявно Любой

Ответ на эту загадку - опция компилятора TypeScript--noImplicitAny:

Вызывает ошибку в выражениях и объявлениях с подразумеваемым типом any.

Эта опция по умолчанию отключена, но у меня есть файл tsconfig.json (конфигурация TypeScript), о котором я забыл, который включает эту опцию;

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react"
  },
  "include": ["./src/**/*"]
}

Согласно BASARAT, это полезно, если вам нужна« высокая безопасность при объявлении аргументов функции в TypeScript».