mobily / ts-belt

🔧 Fast, modern, and practical utility library for FP in TypeScript.
https://mobily.github.io/ts-belt
MIT License
1.1k stars 30 forks source link

Question: Casting type / infering type #54

Closed stychu closed 1 year ago

stychu commented 1 year ago

I'm struggling to understand how to satisfy TypeScript when using ts-belt. Appreciate any help

How do I cast array of numbers from splitting the string? Is this the right approach of such ?

const sample1 = "2x3x4";

_.pipe(
  sample1,
  _.F.coerce<() => [number, number, number]>(_.S.split("x")),
  _.O.flatMap(([l, w, h]) => 2 * l * w + 2 * w * h + 2 * h * l)
);

or

const data = `azs
20x3x11
15x27x5
6x29x7`

const getSurfaceArea = (input: number[]) =>
  _.pipe(
    input,
    _.O.flatMap(([l, w, h]) => {
      if (!l || !w || !h) return 0;

      const squareFeetArea = 2 * l * w + 2 * w * h + 2 * h * l;
      const smallestSide = Math.min(l * w, w * h, h * l);

      return squareFeetArea + smallestSide;
    })
  );

const totalSquareFeet = _.pipe(
  data,
  _.S.split("\n"),
  _.A.map((el) => _.A.map(_.S.split("x")(el), Number).filter(Boolean)),
  _.A.map(getSurfaceArea),
  _.A.reduce(0, _.N.add)
);

console.log(totalSquareFeet);

TS2345: Argument of type '(xs: readonly number[][]) => readonly Option[]' is not assignable to parameter of type '(arg: readonly number[][]) => readonly number[]'.   Type 'readonly Option[]' is not assignable to type 'readonly number[]'.     Type 'Option' is not assignable to type 'number'.       Type 'undefined' is not assignable to type 'number'.

Screenshot 2022-11-21 at 00 46 58

how to handle typing of Option/Result

stychu commented 1 year ago

Clearly I can't wrap my head around how to properly compose functions in order for types to be properly inferred. What I'm doing wrong here so the types don't cascade properly?

const sortAscending = _.A.sort((a: number, b: number) => a - b);

const mapEquations = _.flow(
  _.S.split("\n"),
  _.A.map((el) => _.A.map(_.S.split("x")(el), Number).filter(Boolean))
);

const calculateSum = _.A.reduce([0, 0], (acc, next) => {
  // @ts-ignore
  const paper = acc[0] + next[0];
  // @ts-ignore
  const ribbon = acc[1] + next[1];

  return [paper, ribbon];
});

const calculateSurface = _.O.flatMap(([l, w, h]) => {
  if (!l || !w || !h) return 0;

  const boxSurfaceArea = 2 * l * w + 2 * w * h + 2 * h * l;
  const boxSurface = boxSurfaceArea + l * w;
  const ribbonSurface = 2 * l + 2 * w + l * w * h;

  return [boxSurface, ribbonSurface];
}) as (arr: number[]) => [number, number];

const totalSquareFeet = _.pipe(
  data,
  mapEquations,
  // @ts-ignore
  _.A.map(sortAscending),
  _.A.map(calculateSurface),
  calculateSum
);

console.log(totalSquareFeet);

Screenshot 2022-11-21 at 14 43 09

mobily commented 1 year ago

@stychu hello! 👋 I'm not sure why you're using the Option data type, since none of the utils you use don't return Option (for instance: you use S.split which doesn't return Option)

the solution for the first code snippet:

import { pipe, flow, A, N, S, F } from "@mobily/ts-belt";

const data = `azs
20x3x11
15x27x5
6x29x7`;

const getSurfaceArea = (input: readonly [number, number, number]) => {
  const [l, w, h] = input;
  const squareFeetArea = 2 * l * w + 2 * w * h + 2 * h * l;
  const smallestSide = Math.min(l * w, w * h, h * l);

  return squareFeetArea + smallestSide;
};

const totalSquareFeet = pipe(
  data,
  S.split("\n"),
  A.map(
    flow(
      S.split("x"),
      F.ifElse(
        (xs) => xs.length === 3,
        (values) => {
          return getSurfaceArea(F.coerce(values));
        },
        F.always(0)
      )
    )
  ),
  A.reduce(0, N.add)
);

console.log(totalSquareFeet);
mobily commented 1 year ago

closing due to inactivity, @stychu feel free to reopen if you need help :)