typescript 动态函数返回值类型typing 定义问题
发布于 4 年前 作者 amanda94 5415 次浏览 来自 问答

目前的诉求是 想整个工具函数 把一般的业务函数包一层,业务函数成功执行就返回相应的返回值,如果中间报错了就吞掉错误 返回一个默认值 在javascript里 实现算是满足要求的 代码如下

const assert = require('assert');

function safeCall(fn, defaultValue) {
	defaultValue = defaultValue || null;
	return function realCall(thisCall, ...args){
		try {
			return fn.call(thisCall, ...args);
		} catch (e) {
			return defaultValue;
		}
	};
}

function safeAsyncCall(fn, defaultValue)  {
	defaultValue = defaultValue || null;
	return async function realCall(thisCall, ...args) {
		try {
			return await fn.call(thisCall, ...args);
		} catch (e) {
			return Promise.resolve(defaultValue);
		}
	};
}

// example
const saveJsonParse = safeCall(JSON.parse, { token: null });
const res1 = saveJsonParse(null , '{"token":123}');
const res2 = saveJsonParse(null , '{"token`:888888}');
assert.deepStrictEqual(res1, {token: 123});
assert.deepStrictEqual(res2, {token: null});


async function remoteUnSafeCall(a) {
	if (a) {
		return a;
	}
	throw new Error('not ok')
}
const thisSafeCall = safeAsyncCall(remoteUnSafeCall, null);

thisSafeCall(null, false).then((a) => assert.strictEqual(a, null))

thisSafeCall(null, 2).then((a) => assert.strictEqual(a, 2))

但是在ts 环境中 这种动态的函数 就不能享受typing 的约束 有点尴尬 想着用typing 限制输入输出 在ts 的文档中找到了工具的类型 Parameters<T>ReturnType<T> 然后是类似这样处理

function safeCall<T extends Function >(fn: T, defaultValue: any) {
    defaultValue = defaultValue || null;
    return function realCall(thisCall, ...args: Parameters<typeof fn>): typeof defaultValue | ReturnType<typeof fn> {
        try {
            return fn.call(thisCall, ...args);
        } catch (e) {
            return defaultValue;
        }
    };
}

// use
function sampleCall( params: { a: number; b: string; c: number} ): string {
    return "ddddd";
}
const finalFn = safeCall(sampleCall, "5");
const  res1 = finalFn(null, { a: 1}); // IDE 会有提示 表示输入的参数有问题 
const  res = finalFn(null, { a: 1, b: '12', c: 12}); 
res / 10; // 但是输出的这种错误用法不会报错

尝试这样写之后 除了上面的返回值无法限定 之外 执行的时候 还会报编译错误 搜了下发现 ts认为Function的调用属性不安全 所以加以限制了 然后不得已 定义了个normalFn 的类型 类似:

type NormalFn = (...arg) => any;

function safeCall<T extends NormalFn >(fn: T, defaultValue: any) {
    defaultValue = defaultValue || null;

    return function realCall(thisCall, ...args: Parameters<typeof fn>): typeof defaultValue | ReturnType<typeof fn> {
        try {
            return fn.call(thisCall, ...args);
        } catch (e) {
            return defaultValue;
        }
    };
}

function sampleCall( params: { a: number; b: string; c: number} ): string {
    return "ddddd";
}
const d = safeCall(sampleCall, "5");
let f = d(null, { a: 1, b: '12', c: 12});

f / 10; // 但是输出的这种错误用法不会报错

这次到是不报错了 但是最后的返回值的类型还是没有限制住。。。 有点不知道如何改动了 搜索也不知道用啥关键词 大家有什么想法吗

2 回复
const call = <T extends (...args: any) => any, U>(fn: T, dv: U): ReturnType<T> | U => {
  try {
    return fn();
  } catch (e) {
    return dv;
  }
};

const fn1 = () => 1;
const fn2 = () => 'string';

const run = () => {
  const v1 = call(fn1, true);
  // const v1: number | true
  const v2 = call(fn2, undefined);
  // const v2: string | undefined
};

哦哦 我懂了 是我前面的 defaultValue 设置成 any 了 所以后面的 typeof defaultValue | ReturnType<typeof fn> 就限制不住了 犯了个低级错误。。。 多谢~ @noe132

回到顶部