Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve type guard for .all #81

Open
sindresorhus opened this issue Feb 12, 2019 · 12 comments
Open

Improve type guard for .all #81

sindresorhus opened this issue Feb 12, 2019 · 12 comments
Labels
💵 Funded on Issuehunt This issue has been funded on Issuehunt enhancement help wanted

Comments

@sindresorhus
Copy link
Owner

sindresorhus commented Feb 12, 2019

Issuehunt badges

It would be nice if this worked:

const a = foo();
const b = bar();

// Both `a` and `b` is an `object`, but TS only knows they're `any`.

if (is.all(is.object, a, b)) {
	// `a` and `b` is now known by TS as `object`
}

There is a $30.00 open bounty on this issue. Add more on Issuehunt.

@gioragutt
Copy link
Collaborator

That’s possible I guess if there’s a way to know if the predicate is a type guard predicate

@sindresorhus
Copy link
Owner Author

We don’t need to know it’s a type guard. We can just pass on what TS infers, maybe by using the infer keyword: https://dev.to/miracleblue/how-2-typescript-serious-business-with-typescripts-infer-keyword-40i5

@gioragutt
Copy link
Collaborator

Someone mentions the "built in ReturnType<fn>" type, this might help...

altho, following this issue:

interface Array<T> {
    filter<U extends T>(pred: (a: T) => a is U): U[];
}
type Predicate<T> = (value: unknown) => value is T;

is.all = <T>(predicate: Predicate<T>, ...values: unknown[]): values is T[] =>
  predicateOnArray(Array.prototype.every, predicate, values);

does this look right?

@sindresorhus
Copy link
Owner Author

Looks good. Can you do a PR? :)

@IssueHuntBot
Copy link

@IssueHunt has funded $30.00 to this issue.


@Gerrit0
Copy link

Gerrit0 commented May 9, 2019

I ran into this recently and tried the suggested fix, it looks like this isn't possible generically.

export type Predicate<T = unknown> = (value: unknown) => value is T;

is.all = <T>(predicate: Predicate<T>, ...values: unknown[]): values is T[] =>
	predicateOnArray(Array.prototype.every, predicate, values);

Gives the error A type predicate cannot reference a rest parameter.ts (1229).

Apparently it also isn't possible to do this manually for a few arguments. This is a compile error (and all the other variants I tried).

function test(a: unknown, b: unknown): (a is true) & (b is true) {
    return true;
}

@issuehunt-oss issuehunt-oss bot added the 💵 Funded on Issuehunt This issue has been funded on Issuehunt label May 10, 2019
@gioragutt
Copy link
Collaborator

gioragutt commented May 10, 2019

@Gerrit0 Thanks for looking into it!

to what line does this error refer to? is it thrown from predicateOnArray or all?
Did you try to update predicateOnArray to have the second parameter be of type Predicate<T> as well?

@Gerrit0
Copy link

Gerrit0 commented May 10, 2019

The error is caused by values is T[] on all. Updating Predicate in the predicateOnArray function unfortunately has no effect since the type guard is lost (changed to just be boolean) when it is used in all (or any).

It is possible to achieve the goal in the OP with a different signature. This works as expected (no changes necessary for predicateOnArray).

is.allArray = <T>(predicate: Predicate<T>, values: unknown[]): values is T[] =>
	predicateOnArray(Array.prototype.every, predicate, values)

@nicosayer
Copy link

Any update on this issue ?

@gioragutt
Copy link
Collaborator

@nicosayer I think you can use is.array(value, predicate) for that, does it help you?

@nicosayer
Copy link

nicosayer commented Feb 15, 2022

@nicosayer I think you can use is.array(value, predicate) for that, does it help you?

I am not sure how this helps, can you elaborate ?

To make sure everything is clear, this is how I would summarize the issue:

What is currently happening:

let a: string | number
let b: string | number

if (is.all(is.string, a, b)) {
  // `a` and `b` are of type string | number
}

What is actually expected:

let a: string | number
let b: string | number

if (is.all(is.string, a, b)) {
  // `a` and `b` are of type string
}

@gioragutt
Copy link
Collaborator

@nicosayer yeah my bad, I've been out of context for a while and thought it would work, but it doesn't type-guard the specific array items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💵 Funded on Issuehunt This issue has been funded on Issuehunt enhancement help wanted
Projects
None yet
Development

No branches or pull requests

5 participants