TypeScript:如何为函数中的任何键键入对象剩余扩展

问题描述

我有一个函数,它接受一个对象并返回一个对象。它返回整个传入对象,但添加一个键。物体的形状是未知的,所以它可以有任何键,但它必须有 2 个特定的键。

const myFunction = ({
  num1,num2,...rest
}: {
  num1: number;
  num2: number;
}) => ({
  num1,sum: num1 + num2,...rest,});

myFunction({ num1: 4,num2: 3,foo: 'bar' });
// or myFunction({ num1: 4,baz: 'qux',quux: 'quuz' });

TypeScript 在这里大喊 foo

Argument of type '{ num1: number; num2: number; foo: string; }' is not assignable to parameter of type '{ num1: number; num2: number; }'.
  Object literal may only specify kNown properties,and 'foo' does not exist in type '{ num1: number; num2: number; }

那是简化的例子。

这是我的实际函数以及我如何尝试使用 extends 解决它。

import type { NextApiRequest,NextApiResponse } from 'next';
import { getSession } from 'utils/sessions';

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  },K extends T
>({
  request,response,...rest
}: T): Promise<
  {
    userSession: {
      issuer: string;
      publicAddress: string;
      email: string;
    };
  } & K
> => {
  const userSession = await getSession(request);

  return { request,userSession,...rest };
};

export default withAuthentication;

实际的错误是这样的。

Type '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T,"request" | "response">' is not assignable to type '{ userSession: { issuer: string; publicAddress: string; email: string; }; } & K'.
  Type '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T,"request" | "response">' is not assignable to type 'K'.
    '{ request: NextApiRequest; response: NextApiResponse<any>; userSession: any; } & Omit<T,"request" | "response">' is assignable to the constraint of type 'K',but 'K' Could be instantiated with a different subtype of constraint '{ request: NextApiRequest; response: NextApiResponse<any>; }'.

你怎么能输入这样的函数

解决方法

您可以使用 generics

演示:https://repl.it/@chvolkmann/InternalFrugalCopyleft

interface MyArgs {
  a: number
  b: number
}

const doSomething = <A extends MyArgs>(args: A) => ({
  ...args,sum: args.a + args.b
})

console.log(doSomething({ a: 10,b: 5,foo: 'bar' }))
// Output:
// { a: 10,foo: 'bar',sum: 15 }
,

使用 rest 参数进行解构使其类型检查变得棘手,但如果您只是扩展参数对象并添加 userSession 属性,您最终会得到一个相当可读的解决方案:

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  }
>(arg: T): Promise<{
    userSession: {
      issuer: string;
      publicAddress: string;
      email: string;
    };
  } & T> => {
  const userSession = await getSession(arg.request);
  return { ...arg,userSession };
};

(TypeScript playground)

,

这段代码也可以编译,但我不知道它是否是最好的方法。

import { UserSession } from 'features/user-authentication/types';
import type { NextApiRequest,NextApiResponse } from 'next';
import { getSession } from 'utils/sessions';

const withAuthentication = async <
  T extends {
    request: NextApiRequest;
    response: NextApiResponse;
  }
>({
  request,response,...rest
}: T): Promise<
  {
    request: NextApiRequest;
    response: NextApiResponse;
    userSession: UserSession;
  } & Omit<T,'request' | 'response'>
> => {
  const userSession = await getSession(request);

  if (userSession) {
    return { request,userSession,...rest };
  }

  throw new Error('Unauthenticated');
};

export default withAuthentication;

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...