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

How to set default value to param that has type keyof (from an explicit type) in Typescript

我在 Typescript 将默认值传递给类似于 lodash.

中的 pick 的函数时遇到问题

该函数接受一个已知(非通用)接口的对象和一组要从对象中选择和 return 的键。

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

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,它会成功编译,并且 returned 类型在示例调用中也是正确的,例如: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 参数?

打字稿游乐场 link 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

类似的东西? Updated TS Playground.

const defaultProps: ReadonlyArray<keyof Person> = ['name', 'age'] as const;

function pick<T extends keyof Person>(obj: Person, props: ReadonlyArray<T> = (defaultProps as ReadonlyArray<T>)): Pick<Person, T> {   

更新:

TS 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

您可以在我的 article 和其他答案中找到更高级的 pick 类型: , , 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