#typescript #return-type #conditional-types
#typescript #возвращаемый тип #условные типы
Вопрос:
У меня есть небольшая служебная функция, и теперь я пытаюсь написать для нее немного более конкретный тип, используя дженерики.
Вот код:
/* global localStorage */
const ls = localStorage
export type ParsedJSON<T> = T | null
export type Falsy = false | undefined
function get<T = any> (
key: string,
parseJSON?: boolean
): typeof parseJSON extends Falsy ? string : ParsedJSON<T> {
const value = ls.getItem(key) ?? ''
if (parseJSON === true) {
let result: ParsedJSON<T> = null
try {
result = JSON.parse(value)
} catch (err) {}
return result
} else if ((parseJSON === false)) {
return value
}
return null
}
У меня ошибка для ветки else : Type 'string' is not assignable to type 'ParsedJSON<T>'
.
(Ветвь else здесь немного избыточна, но я добавил ее явно для расследования этой ошибки).
Я не могу понять, почему TS пытается сопоставить string
тип с ParsedJSON<T>
типом.
Единственное разумное объяснение для меня заключается в том, что false
это не может быть присвоено Falsy
типу, поэтому возвращаемый тип вычисляется как ParsedJSON<T>
. Но я проверил, что это не так:
Поэтому, пожалуйста, помогите мне понять, что здесь происходит. ) Спасибо.
Комментарии:
1. Это не решение этой проблемы, но я думаю, вам следует рассмотреть возможность перегрузки функций
Ответ №1:
Похоже, что происходит то, что TS вычисляет выражение типа typeof parseJSON extends Falsy ? string : ParsedJSON<T>
не как условное выражение, подлежащее вычислению с общими параметрами при вызове функции, а сразу на основе доступной в данный момент информации о функции.
Зная это, мы можем определить, каким компилятор определяет возвращаемый тип. Выражение
typeof parseJSON extends Falsy ? string : ParsedJSON<T>
parseJSON
тип во время вычисления равен boolean | undefined
, поэтому он не расширяет Falsy
то, что есть false | undefined
, и, следовательно, является более узким типом.
Поэтому get
тип возвращаемого значения определяется как простой: ParsedJSON<T>
.
Единственный способ, которым я могу придумать условный возвращаемый тип, основанный на фактических значениях параметров на сайте вызова, — это, как предположил @Nishant, перегрузка функций. Например.:
function get<T = any>(key: string, parseJSON: true): ParsedJSON<T>
function get<T = any>(key: string, parseJSON?: false): string
function get<T = any>(key: string, parseJSON?: boolean) {
const value = ls.getItem(key) ?? ''
if (parseJSON === true) {
let result: ParsedJSON<T> = null
try {
result = JSON.parse(value)
} catch (err) { }
return result
} else if ((parseJSON === false)) {
return value
}
return null
}
Теперь функция будет иметь правильный возвращаемый тип на основе parseJSON
‘s type .
get<number>('', true) // => ParsedJSON<number>
get<number>('', false) // => string
get<number>('') // => string