我可以告诉TypeScript我的函数返回一个类型,它的一个参数返回?

gv8xihay  于 7个月前  发布在  TypeScript
关注(0)|答案(2)|浏览(109)

假设我有一个函数,它访问对象中的值。

// TypeScript
function access<T, K extends keyof T>(object: T, key: K): T[K] {
    return object[key];
}

const object: Record<string, any> = {
    zero: 0,
    one: 1,
    two: "2",
};
const one = access(object, "one");
const two = access(object, "two");

字符串
有了这个,TypeScript可以计算出one是一个数字,two是一个字符串。到目前为止一切顺利。
但是现在,假设我想潜在地修改我在access函数中获得的键。

// JavaScript
function identity(x) {
    return x;
}

function access(object, key, modify = identity) {
    return modify(object[key]);
}

const object = {
    zero: 0,
    one: 1,
    two: "2",
};
const zero = access(object, "zero", Boolean);
const one = access(object, "one");
const two = access(object, "two", Number);


有没有一种方法可以告诉TypeScript zero是一个布尔值,one是一个数字,two也是一个数字?比如,我可以告诉TypeScript我的access函数将返回一个modify参数返回的类型吗?
我试过这样的方法:

function identity(x: any): typeof x {
    return x;
}

function access<T, K extends keyof T, V extends any>(object: T, key: K, modify: (x: V) => typeof x = identity): V {
    return modify(object[key]);
}


虽然这正确地识别了zerotwo,但one仍然是未知的。
我也试过这个:

function access<T, K extends keyof T, V extends T[K]>(object: T, key: K, modify: (x: V) => typeof x = identity): V {
    return modify(object[key]);
}


但这会将第三个参数标记为错误,因为它期望modify函数返回与给定类型相同的类型。
有没有一种TypeScript的方法来做到这一点,或者我只是把事情复杂化了?

4nkexdtk

4nkexdtk1#

如果我们不担心将identity用作default function argument,那么正确的类型如下所示:

function access<T extends object, K extends keyof T, R>(
  object: T, key: K, modify: (v: T[K]) => R) {
  return modify(object[key]);
}

字符串
这里我们只是添加了一个新的类型R,对应于modify的返回类型,其中modify的输入类型是indexed access typeT[K]。它的行为与预期的一样:

const object = {
  zero: 0,
  one: 1,
  two: "2",
};
function identity<T>(x: T) {
  return x;
}

const zero = access(object, "zero", Boolean); // boolean
const one = access(object, "one", identity); // number
const two = access(object, "two", Number); // number


当然,如果你需要的话,你需要传入identity
当尝试使用identity作为默认参数时,它会变得更加复杂。TypeScript中存在断开连接,这使得很难使用带有默认函数参数的generics。默认类型参数并不完全符合大多数人的期望。问题与 * 调用者 * 选择类型参数的事实有关,但是在这里,我们希望 * 实现 * 选择它,如果有人没有传入modify属性。它就是不这样工作。在microsoft/TypeScript#56315有一个开放的功能请求,要求更好的东西。
除非发生这种情况,否则最简单的解决方法是使用默认的泛型类型参数(当modifyindentity时,那么上面的R应该是T[K],所以我们默认为R = T[K]),然后Assertidentity是必要的类型(v: T[K]) => R

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

function access<T extends object, K extends keyof T, R = T[K]>(
  object: T, key: K, modify = identity as (v: T[K]) => R) {
  return modify(object[key]);
}


这仍然是你想要的方式:

const object = {
  zero: 0,
  one: 1,
  two: "2",
};
const zero = access(object, "zero", Boolean);
const one = access(object, "one");
const two = access(object, "two", Number);


(The需要类型Assert的原因是没有什么能阻止疯狂的人显式地指定类型参数,
const crazy = access<type of object,“one”,string>(object,“one”); // const crazy:string
这是一件很奇怪的事情。但是编译器不能确定这不会发生。)
Playground链接到代码

vnzz0bqm

vnzz0bqm2#

是的,在TypeScript中,您可以使用ReturnType实用程序类型定义函数的返回类型,键为引用其参数之一的属性。例如:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const person = {
    name: 'John',
    age: 30
};

const name: string = getProperty(person, 'name');
const age: number = getProperty(person, 'age');

字符串
在此示例中,getProperty函数返回与其参数的指定属性的类型匹配的类型。

相关问题