У меня есть следующая функция:
function demo<T>(init: T) {
return init;
}
Я хотел бы дать init значение по умолчанию {}, если оно опущено. Итак, я написал:
function demo2<T>(init: T = {}) {
return init;
}
Но, конечно, это дает мне ошибку:
Type '{}' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{}'.(2322)
Как я могу заставить T быть {}, если параметр опущен? Спасибо.
@ Анатолий Да, но есть ли способ автоматически изменить тип пустого объекта на T, когда параметр опущен?
Как? Если вы используете useListVariables<Derived>, вы не можете использовать {} в качестве значения по умолчанию.
Не могли бы вы отредактировать этот код, чтобы он был минимально воспроизводимым примером , подходящим для добавления в автономную IDE, такую как The TypeScript Playground? Прежде чем публиковать ответы, мне хотелось бы иметь возможность проверить их на примере варианта использования, но я не знаю, что такое ListVariables, и я не уверен, какие результаты вы хотите увидеть для различных вариантов использования useListVariables() (например, что, если кто-то вручную указывает параметр типа, а не позволяет компилятору вывести его). Любой код, который показывает, что вы пробовали. Спасибо!
@jcalz Готово!






Ошибка технически верна; вызывающим сторонам вашей функции demo2() разрешено указывать параметр типа T таким, каким они хотят его видеть. Это означает, что вызов, подобный следующему, не приведет к ошибке компилятора, но легко приведет к ошибке времени выполнения:
const z2 = demo2<{ a: number }>(); // uh oh, no error
z2.a.toFixed(); // no error at compile time but "z2.a is undefined" at runtime
Тем не менее, если вы считаете, что вероятность того, что кто-то сделает это (ручное указание параметра универсального типа) низка, вы можете использовать утверждение типа для подавления ошибки, в то же время используя универсальный параметр по умолчанию, поэтому что компилятор выведет {} вместо T, если вы пропустите свойство init:
// assertion with default type parameter
function demo3<T = {}>(init: T = {} as T) {
return init;
}
Это дает вам следующее желаемое поведение:
const x3 = demo3(); // {}
const y3 = demo3({ a: 123 }); // {a: number}
при этом допуская следующее нежелательное поведение:
const z3 = demo3<{ a: number }>(); // no compiler error
z3.a.toFixed(); // RUNTIME ERROR!
Если вы хотите запретить указание T вручную без передачи значения типа T, вы можете рассмотреть возможность использования чего-то вроде перегруженной функции с несколькими сигнатурами вызова:
// перегрузка
function demo4(): {};
function demo4<T>(init: T): T;
function demo4(init = {}) {
return init;
}
Здесь, если вызывающая сторона пропускает параметр init, функция больше не рассматривается как универсальная; тип возврата просто {}. С другой стороны, если вызывающая сторона предоставляет параметр init, то функция обрабатывается так же, как ваша исходная функция demo(). Реализация функции работает для любой сигнатуры вызова.
Это также приводит к желательному поведению для «обычных» вызовов:
const x4 = demo4(); // {}
const y4 = demo4({ a: 123 }); // {a: number}
А также выдает предупреждение компилятору, если кто-то попытается назвать это неправильно:
const z4 = demo4<{ a: number }>(); // compiler error! an argument for init is required
вы не можете гарантировать, что расширенный тип также будет частичным.
interface Derived extends Partial<ListVariables>{ bbb: string; }. Попробуйте передать переменную этого типа.