Skip to content

Conversation

@Milly
Copy link
Collaborator

@Milly Milly commented Apr 13, 2024

Motivation

I want to validate method arguments with optional parameters.

Comparison with isTupleOf()

  • isTupleOf(): the length of the tuple is fixed.
const a: unknown = ["a"];
if (is.TupleOf([is.String, is.Optional(is.Number)] as const)(a)) {
  // don't reach here
  const _: [string, number | undefined] = a;
}
  • isParametersOf(): the tuple is truncated length if trailing predicates are optional.
const a: unknown = ["a"];
if (is.ParametersOf([is.String, is.Optional(is.Number)] as const)(a)) {
  // reach here
  const _: [string, number?] = a;
}

Naming

If there is a better name, please suggest.

@lambdalisue
Copy link
Member

It seems the function is quite resemble with isTupleOf with predElse.

import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";

const isMyType = is.TupleOf(
  [is.Number, is.String, is.Boolean],
  is.ArrayOf(is.Number),
);
const a: unknown = [0, "a", true, 0, 1, 2];
if (isMyType(a)) {
  // a is narrowed to [number, string, boolean, ...number[]]
  const _: [number, string, boolean, ...number[]] = a;
}

What is the difference and advantage compared to the existing one?

@Milly
Copy link
Collaborator Author

Milly commented Apr 13, 2024

How can I use isTupleOf() with predElse to resolve the following args type?

It is possible by defining a custom Predicate isOpts, but I would like to avoid implementing type guards.

import { is } from "https://deno.land/x/unknownutil@$MODULE_VERSION/mod.ts";

function f(name: string, optCount?: number, optFlag?: boolean): void {}

const arglist: unknown[] = [
  ["a", 1, true],
  ["a", 1],
  ["a"],
  ["a", true, 1], // Expect skip
  ["a", 1, true, true, true], // Expect skip
];

for (const args of arglist) {
  if (is.TupleOf([is.String] as const, is.ArrayOf(is.UnionOf([is.Number, is.Boolean])))(args)) {
    // args is narrowed to [string, ...(number, boolean)[]]
    f(...args); // deno-ts:Error:2345:Argument of type 'number | boolean' is not assignable to parameter of type 'number | undefined'. Type 'boolean' is not assignable to type 'number'.
  }

  if (is.UnionOf([is.TupleOf([is.String]), is.TupleOf([is.String, is.Number]), is.TupleOf([is.String, is.Number, is.Boolean])])(args)) {
    // args is narrowed to [string] | [string, number] | [string, number, boolean]
    f(...args); // deno-ts:Error:2556:A spread argument must either have a tuple type or be passed to a rest parameter.
  }

  const isOpts = (_x: unknown): _x is [number?, boolean?] => /* nice implements */ true;
  if (is.TupleOf([is.String], isOpts)(args)) {
    // args is narrowed to [string, (number | undefined)?, (boolean | undefined)?]
    f(...args); // No error
  }

  if (is.ParametersOf([is.String, is.OptionalOf(is.Number), is.OptionalOf(is.Boolean)] as const)(args)) {
    // args is narrowed to [string, (number | undefined)?, (boolean | undefined)?]
    f(...args); // No error
  }
}

@lambdalisue
Copy link
Member

Ah got it. 

Copy link
Member

@lambdalisue lambdalisue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to make the description a bit more clear but LGTM.

Copy link
Member

@lambdalisue lambdalisue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@lambdalisue lambdalisue merged commit d94d0c2 into jsr-core:main Apr 14, 2024
@Milly Milly deleted the parametersof branch April 14, 2024 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants