TypeScript:根据类型返回不同的函数

发布于 2025-01-11 12:49:53 字数 1706 浏览 0 评论 0原文

我想要实现的是一个钩子,它返回一个我可以调用的函数。

type TypeName = 'circle' | 'square';

interface Circle {
  type: TypeName;
  radius: number;
}

interface Square {
  type: TypeName;
  length: number;
}

type Shape = Circle | Square;

const useShapeFilter = (shape: Shape) => {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  return {applyFilter: shape.type === 'circle' ? circleFilter : shape.type === 'square' ? squareFilter : undefined}
}

export const ShapeComponent = () => {
  const shape: Circle = {type: 'circle', radius: 10}
  const {applyFilter} = useShapeFilter(shape);

  return <div>Less than 5 : {applyFilter && applyFilter(shape)}</div>
}

但上面的代码将在 applyFilter(shape) 区域返回 TypeScript 错误: Argument of type 'Circle' is not allocate to argument of type 'Circle &正方形'。 “Circle”类型中缺少属性“length”,但“Square”类型中需要属性“length”。

最后,我不得不改变实现并断言数据:

const useShapeFilter = (shape: Shape) => {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  if (shape.type === 'circle') {
    return circleFilter(shape as Circle)
  }
  if (shape.type === 'square') {
    return squareFilter(shape as Square)
  }
  return undefined;

}

export const AnimalPage = () => {
  const shape: Circle = {type: 'circle', radius: 10}
  const filtered = useShapeFilter(shape);

  return <div>Less than 5 : {filtered}</div>
}

但我仍然想知道如何实现我的初衷。

What I want to achieve is a hook that returns me a function that I can call.

type TypeName = 'circle' | 'square';

interface Circle {
  type: TypeName;
  radius: number;
}

interface Square {
  type: TypeName;
  length: number;
}

type Shape = Circle | Square;

const useShapeFilter = (shape: Shape) => {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  return {applyFilter: shape.type === 'circle' ? circleFilter : shape.type === 'square' ? squareFilter : undefined}
}

export const ShapeComponent = () => {
  const shape: Circle = {type: 'circle', radius: 10}
  const {applyFilter} = useShapeFilter(shape);

  return <div>Less than 5 : {applyFilter && applyFilter(shape)}</div>
}

But the above will return a TypeScript error at the applyFilter(shape) area: Argument of type 'Circle' is not assignable to parameter of type 'Circle & Square'. Property 'length' is missing in type 'Circle' but required in type 'Square'..

In the end, I had to change the implementation and assert the data:

const useShapeFilter = (shape: Shape) => {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  if (shape.type === 'circle') {
    return circleFilter(shape as Circle)
  }
  if (shape.type === 'square') {
    return squareFilter(shape as Square)
  }
  return undefined;

}

export const AnimalPage = () => {
  const shape: Circle = {type: 'circle', radius: 10}
  const filtered = useShapeFilter(shape);

  return <div>Less than 5 : {filtered}</div>
}

But I'm still wondering how to achieve my original intention.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

恬淡成诗 2025-01-18 12:49:53

由于您返回的函数联合与原始参数没有任何关系,因此打字稿无法真正遵循 shape 实际上适合 applyFilter 的事实。一般来说,如果一个变量可以保存两个函数中的任何一个,那么只有使用该变量调用该变量才是安全的,并且参数对于任一潜在的底层函数都有效。

为了解决这个问题,我们可以使 useShapeFilter 通用,捕获原始参数,并返回一个与该特定参数兼容的函数:

function useShapeFilter<T extends Shape>(shape: T): { applyFilter: (shape: T) => boolean; } 
function useShapeFilter(shape: Shape) {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  return {applyFilter: shape.type === 'circle' ? circleFilter : shape.type === 'square' ? squareFilter : undefined}
}

游乐场链接

Since you are returning a union of functions without any relation to the original parameter, typescript can't really follow the fact that shape will actually fit into applyFilter. Generally if a variable can hold any one of two functions it is only safe to call the variable with and argument would be valid for EITHER one of the potential underlying functions.

To get around this we could make useShapeFilter generic, capture the original parameter, and return a function that will be compatible with that specific argument:

function useShapeFilter<T extends Shape>(shape: T): { applyFilter: (shape: T) => boolean; } 
function useShapeFilter(shape: Shape) {
  const circleFilter = (circle: Circle) => (circle.radius < 5);

  const squareFilter = (square: Square) => (square.length < 5);

  return {applyFilter: shape.type === 'circle' ? circleFilter : shape.type === 'square' ? squareFilter : undefined}
}

Playground Link

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文