Функция, принимающая функцию, возвращающую универсальный тип, небезопасна для типов

#typescript-generics

#typescript-дженерики

Вопрос:

Я сталкиваюсь с этим при использовании перехватов React, но это общий вопрос TypeScript.

Вы можете увидеть это на детской площадке

Когда я использую общий параметр, который я нахожу наиболее интуитивно понятным, функция, которую я передаю, не является полностью типизированной. Пока она возвращает объект с реквизитами в T, компилятор доволен, но я также могу добавить реквизит, который не является частью T. Это становится проблемой, когда у T есть некоторые дополнительные реквизиты, потому что я могу пропустить их по буквам и не знать об этом.

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

Может кто-нибудь, пожалуйста, объяснить, почему компилятор допускает это?

 const foo = <T>(f: ()=>T) => {
    return f();
};

type Result = {
    bar: number;
    foo?:number;
}

const strange = foo<Result>(() => {
    return {
        bar: 42,
        baz: 12, // why is this prop allowd?
        FOO: 13 // ups I might think I set the foo prop, but I spelled it wrong
    };
});

const expected = foo((): Result => {
    return {
        bar:42,
        FOO: 12 // this is not allowd, which is what I wxpected
    };
});
 

Ответ №1:

Проверка избыточного свойства запускается только для «свежего» объектного литерала. Свежесть объектного литерала теряется в случае утверждения типа и расширения типа.

в strange функции тип возвращаемого типа расширяется (см. Выпуск № 241). Таким образом, избыточные свойства допустимы.

 const strange = foo<Result>(() => {
    // type widening occurs here
    // freshness is lost
    // No excess property check triggered
    return {
        bar: 42,
        baz: 12, // excess property allowed
        FOO: 13  // excess property allowed
    };
});
 

в expected функции нет расширения типа, поскольку возвращаемый тип явно указан. свежесть объектного литерала сохраняется, и запускается проверка избыточного свойства

 const expected = foo((): Result => {
    // No type assertion
    // object literal is fresh
    // Excess property check is triggered
    return {
        bar:42,
        FOO: 12 // this is not allowd, which is what I wxpected
    };
});
 

Комментарии:

1. Спасибо за ваш ответ. Знаете ли вы, почему это так? В каких ситуациях вы действительно хотите такого поведения?

2. Без расширения возвращаемого типа это приведет к сбою : const a = () => ({ x: 3, y: 4 }); const b: () => { x: number } = a .

3. Да, и я бы хотел этого, потому что a возвращает что-то, что не является a { x: number } . Если бы я хотел использовать a, как вы предложили, я бы добавил [key: string]: any к возвращаемому типу b .