typescript - 如何在 Typescript 中将默认值设置为具有类型 keyof(来自显式类型)的参数

标签 typescript typescript-generics default-parameters keyof

我在 Typescript 将默认值传递给类似于 lodashpick 的函数时遇到问题。

该函数接受一个已知(非通用)接口(interface)的对象和一组从该对象中选取和返回的键。

函数的常规(无默认参数)声明工作正常,但是,我似乎无法将数组设置为选择属性的参数的默认值

interface Person {
    name: string;
    age: number;
    address: string;
    phone: string;
}

const defaultProps = ['name', 'age'] as const;


function pick<T extends keyof Person>(obj: Person, props: ReadonlyArray<T> = defaultProps): Pick<Person, T> {    
    return props.reduce((res, prop) => {
        res[prop] = obj[prop];
        return res;
    }, {} as Pick<Person,T>);
}

const testPerson: Person = {
    name: 'mitsos',
    age: 33,
    address: 'GRC',
    phone: '000'
};

如果您删除默认值 = defaultProps,它会成功编译,并且从示例调用返回的类型也是正确的,例如:const testPick = pick(testPerson, ['name'] );

但是,设置默认值会产生以下错误:

Type 'readonly ["name", "age"]' is not assignable to type 'readonly T[]'.
  Type '"name" | "age"' is not assignable to type 'T'.
    '"name" | "age"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'keyof Person'.
      Type '"name"' is not assignable to type 'T'.
        '"name"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'keyof Person'.

如何将默认值成功传递给 props 参数?

Typescript Playground 链接 here

更新

玩了一会儿之后,我尝试使用条件类型并设法使函数签名正常工作,但现在无法正确识别 reduce 的问题:

interface Person {
    name: string;
    age: number;
    address: string;
    phone: string;
}

const defaultProps = ['name', 'age'] as const;

type DefaultProps = typeof defaultProps;

type PropsOrDefault<T extends keyof Person> = DefaultProps | ReadonlyArray<T>;

type PickedPropOrDefault<T extends PropsOrDefault<keyof Person>> = T extends DefaultProps ? Pick<Person, DefaultProps[number]> : Pick<Person, T[number]>;


function pick<T extends keyof Person>(obj: Person, props: PropsOrDefault<T> = defaultProps): PickedPropOrDefault<PropsOrDefault<T>> {    
    return props.reduce<PickedPropOrDefault<PropsOrDefault<T>>>((res, prop) => {
        res[prop] = obj[prop];
        return res;
    }, {} as PickedPropOrDefault<PropsOrDefault<T>>);
}

const testPerson: Person = {
    name: 'mitsos',
    age: 33,
    address: 'GRC',
    phone: '000'
};

const result = pick(testPerson) //  Pick<Person, "name" | "age">
const result2 = pick(testPerson, ['phone']) // Pick<Person, "phone">
const result3 = pick(testPerson, ['abc']) // expected error

Updated Playground

最佳答案

您可以重载 pick功能:

interface Person {
    name: string;
    age: number;
    address: string;
    phone: string;
}

const defaultProps = ['name', 'age'] as const;

type DefaultProps = typeof defaultProps;

function pick(obj: Person): Pick<Person, DefaultProps[number]>
function pick<Prop extends keyof Person, Props extends ReadonlyArray<Prop>>(obj: Person, props: Props): Pick<Person, Props[number]>
function pick<T extends keyof Person>(obj: Person, props = defaultProps) {
    return props.reduce((res, prop) => ({
        ...res,
        [prop]: obj[prop]
    }), {} as Pick<Person, T>);
}

const testPerson = {
    name: 'mitsos',
    age: 33,
    address: 'GRC',
    phone: '000'
};

const result = pick(testPerson) //  Pick<Person, "name" | "age">
const result2 = pick(testPerson, ['phone']) // Pick<Person, "phone">
const result3 = pick(testPerson, ['abc']) // expected error

Playground

你可以找到更高级的pick在我的 article 中打字和其他答案: First , second , third

更新

props 有问题此代码中的参数:

function pick<T extends keyof Person>(obj: Person, props: PropsOrDefault<T> = defaultProps): PickedPropOrDefault<PropsOrDefault<T>> {
    return props.reduce((res, prop) => {
        return {
            ...res,
            [prop]: obj[prop]
        }
    }, {});
}

PropsOrDefault可能等于此类型:type UnsafeReduceUnion = DefaultProps | ReadonlyArray<'phone' | 'address'> 您可能已经注意到,联合中的这些数组是完全不同的。他们没有任何共同点。

如果你想调用reduce :

declare var unsafe:UnsafeReduceUnion;

unsafe.reduce()

你会得到一个错误,因为reducenot callable

关于typescript - 如何在 Typescript 中将默认值设置为具有类型 keyof(来自显式类型)的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69680545/

相关文章:

javascript - Angular 2为每个数组对象添加键和值

typescript - 无法生成 Jest 覆盖率报告

typescript - 接受特定类型对象的 keyof 吗?

Fortran 2003/2008 : Elegant default arguments?

c++ - 将重载函数分配给函数指针作为默认值

swift - 默认参数

angularjs - disabled=false 在 typescript 2.1 中不起作用

javascript - 需要获取所有子组件实例的所有值并在父方法: ReactJS中使用它

typescript - 是否可以在 TypeScript 中精确键入 _.invert?

reactjs - typescript + React : define a generic table component