Typescript:包装函数可防止推断出匿名函数的键入

时间:2018-11-15 23:11:09

标签: typescript type-inference

我遇到打字麻烦:

function identity<T>(v: T): T{ return v; }

function execute(fn: {(n: number):string}) {}

execute((n) => {
    // type of n is 'number'
    return n.toFixed();
})

execute(identity((n) => {
    // type of n is 'any'
    return n.toFixed();
}))

当键入的高阶函数execute接收到一个函数时,该匿名函数的参数将通过推断来键入。但是,将该匿名函数传递给包装器identity会导致这些推断的类型丢失。我可以对executeidentity的结构进行任何调整,以允许仍推断出这些类型吗?

注意为简单起见,identity在这里是一个纯函数。实际上,它不是,但是应该具有与此identity函数相同的类型。有关问题的更多信息,请参见checkpoint

TS Playground

中查看

上下文

这是我在React组件生命周期的上下文中加载数据时遇到的问题的一般形式。因为不应该在不再安装的组件上调用setState,所以可以防止触发加载回调。

function loadData():Promise<MyDataType> {/*...*/}    

// Wraps the passed function (handleDataLoaded), 
// such that when the returned function is executed the 
// passed function is conditionally executed depending 
// on closure state.
function checkpoint(fn){/*...*/}

// Add data to the state of the component
function handleDataLoaded(val: MyDataType){/*...*/}



// react lifecycle hook componentDidMount
    loadData()
        .then(checkpoint(handleDataLoaded));

// react lifecycle hook componentWillUnmount 
// adjusts state of checkpoint's closure such that handleDataloaded
// is not fired after componentWillUnmount

2 个答案:

答案 0 :(得分:1)

您写的实际上与以下内容相同:

function identity<T>(v: T): T{ return v; }

function execute(fn: {(n: number):string}) {}

execute((n) => {
    // type of n is 'number'
    return n.toFixed();
})

var func = identity((n) => {
    // type of n is 'any'
    return n.toFixed();
});
execute(func);

否,当您明确提供通用参数时:

var func = identity<number>((n) => {
    // type of n is 'any'
    return n.toFixed();
});

您将收到编译器错误:

enter image description here

现在,您看到的是传递的是函数而不是数字。

如果您解释您要做什么,我们也许可以为您提供解决方案。

答案 1 :(得分:1)

绝对没有麻烦。这更像是您在逻辑上遇到了一些错误(在您的脑海中)。不使用严格模式是另一个问题。

/* execute( */  identity((n) => {
    // type of n is 'any', why wouldn't it be?
    // There is no type constraint in `identity` function, 
    // hence you have absolutely no reason to expect `n` to have type `number` 
    // I commented out the wrapping by `execute` function 
    // so that it doesn't confuse you. Because, no matter
    // if it's there or not, you should first figure out
    // the shape and type of underlying expression,
    // because this is how Typescript figures them out.
    return n.toFixed();
}) /* ) */

但是

function identity<T extends {(n: number): string}>(v: T): T{ return v; }

/* execute( */ identity((n) => {
    // type of n is 'number', because we added a constraint to the type parameter `T` in `identity` function
    return n.toFixed();
}) /* ) */ 

您也可以这样做:

/* execute( */ identity<{(n: number): string}>((n) => {
    // type of n is 'number'
    return n.toFixed();
}) /* ) */

execute(identity((n: string) => {
    // this is a TS error
    // "Argument of type '(n: string) => () => string' is not 
    // assignable to parameter of type '(n: number) => string'"
    return n.toFixed;
}))

最后,您应该始终使用严格模式(将"strict": true添加到tsconfig.json的“ compilerOptions”中),您将永远不会遇到此类警告。