Вывод типа переменных по-разному - PullRequest
0 голосов
/ 06 января 2019

Рассмотрим код ниже (работает в TypeScript 3.2.2):

const status: 'successful' | 'failed' = 'successful';

function test(): typeof status {
  const status = 'hello';

  return 'successful';
}

Не компилируется, поскольку тип возвращаемого значения test и его подпись не совпадают:

error TS2322: Type '"successful"' is not assignable to type 'IResult<"hello">'.

По некоторым причинам status определение внутри функция используется для определения типа возвращаемого значения.

Этого не происходит при использовании var; этот код:

function test(): typeof status {
  var status = 'hello'; // notice the var here

  return 'successful';
}

создает ожидаемый тип возврата 'successful' | 'failed'.

Использование let:

function test(): typeof status {
  let status = 'hello'; // notice the let here

  return 'successful';
} 

Компилируется, но с учетом того, что тип возвращаемого значения равен string, внутреннее определение используется снова.

Я ожидал, что tsc будет использовать status, который определен наиболее высоко в областях для оценки его возвращаемого типа в обоих случаях, независимо от того, какие объявления существуют внутри test. Почему такое поведение наблюдается выше? Это должно быть связано с тем, как tsc решает, какие переменные использовать для вывода типа.

1 Ответ

0 голосов
/ 06 января 2019

Здесь 12 случаев: (const, let, var) * (global, local) * (explicit string union, inferred string)

TL; DR

Простой подход:

  • если вы указываете тип явно и во внешней области видимости - результат всегда одинаков: объединение "ok" | "no".

Гоча:

  • , поскольку внутренние let и const доступны для typeof в возвращаемой позиции, они скрывают внешнюю декларацию.
  • внутренняя var недоступна для typeof в позиции типа возврата (по любой причине)

Добавить дополнительную путаницу:

  • , если вы попытаетесь сделать вывод: let и var будут считать тип string, а const будет набирать точное значение [неизменный оригинал].

Список дел для справки:

// Infer, Inner
function test_inner_let(): typeof status01 { // type is string
  let status01 = 'ok'; // let is mutable, so type is only string
  return 'lol';
}
function test_inner_const(): typeof status02 { // type is 'ok'
  const status02 = 'ok'; // const allows to specify type to exact 'ok'
  return 'lol'; // error, 'lol' is not assignable to 'ok'
}
function test_inner_var(): typeof status03 { // type is any, TS warning: status03 not found
  var status03 = 'ok'; // var is mutable, so type is string
  return 'lol';
}

// Explicit, Inner
function test_inner_let_t(): typeof status11 { // type is union 'ok'|'no'
  let status11: 'ok' | 'no' = 'ok';
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}
function test_inner_const_t(): typeof status12 { // type is union 'ok'|'no'
  const status12: 'ok' | 'no' = 'ok';
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}
function test_inner_var_t(): typeof status13 { // type is any, TS warning: status13 not found
  var status13: 'ok' | 'no' = 'ok';
  return 'lol';
}

// Explicit, Outer - everything works the same
let status21: 'ok' | 'no' = 'ok';
function test_outer_let_t(): typeof status21 { // type is union 'ok'|'no'
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}
const status22: 'ok' | 'no' = 'ok';
function test_outer_const_t(): typeof status22 { // type is union 'ok'|'no'
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}
var status23: 'ok' | 'no' = 'ok';
function test_outer_var_t(): typeof status23 { // type is union 'ok'|'no'
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}

// Infer, Outer
let status31 = 'ok'; // type is string
function test_outer_let(): typeof status31 { // type is string
  return 'lol';
}
const status32 = 'ok'; // const allows to specify type to exact 'ok'
function test_outer_const(): typeof status32 { // type is 'ok'
  return 'lol'; // error, 'lol' is not assignable to 'ok'
}
var status33 = 'ok'; // var is mutable, so type is string
function test_outer_var(): typeof status33 { // type is string
  return 'lol';
}

// (Explicit, Outer const) + (Implicit, Inner)
const status41: 'ok' | 'no' = 'ok';
function test_combo_let(): typeof status41 { // type is string, inner let took preference
  let status41 = 'ok';
  return 'lol';
}
const status42: 'ok' | 'no' = 'ok';
function test_combo_const(): typeof status42 { // type is 'sorry', inner const took preference
  const status42 = 'sorry';
  return 'lol'; // error, 'lol' is not assignable to 'sorry'
}
const status43: 'ok' | 'no' = 'ok';
function test_combo_var(): typeof status43 { // type is union 'ok'|'no', var is not bubling up
  var status = 'whatever';
  return 'lol'; // error, 'lol' is not assignable to 'ok'|'no'
}
...