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

"number[] & [any]" should be "[number]" #37926

Closed
eyedean opened this issue Apr 13, 2020 · 4 comments
Closed

"number[] & [any]" should be "[number]" #37926

eyedean opened this issue Apr 13, 2020 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@eyedean
Copy link

eyedean commented Apr 13, 2020

I am under the impression that tuple is a fixed-length array. Given that, if I intersect a typed array with a fixed-length tuple, I should get a fixed-length and typed tuple, where the length is derived from the former and the type form the latter.

Unfortunately, it doesn't work as intended.

TypeScript Version: 3.8.3

Search Terms: tuple, array, intersection, union

Code

type t = number[] & [any];

let a = [1] as t;
let x = a[0];

console.log(typeof x); // expected to be number, but is any

Playground Link

Expected behavior: typeof x should be number

Actual behavior: typeof x is any

Playground Link:

Related Issues: I found #30895 but that's a different issue.


History/Context: I want to write a predicate for isSingleElementArray(x: unknown) function and I thought x is [unknown] should work, but it doesn't. See the example below:

function isSingleElementArray(x: unknown): x is [unknown] {
	return Array.isArray(x) && x.length === 1;
}

const b = [1]; // b's type is number[] as expected
if (isSingleElementArray(b)) {
    const c = b[0]; // c's type is "unknown"
    console.log(c + 2); // ERROR: object is of type unknown
}

Playground Link

If I change the predicate to x is unknown[] it works fine, but obviously it won't consider/communicate the length anymore.

@RyanCavanaugh
Copy link
Member

You could write number[] & { length: 1 } instead and this works as expected.

Currently intersections don't have any special rules to produce new types; it's not clear this would be desirable - for an intersection of N elements we'd have to do N * (N - 1) comparisons to see if any "special" rules applied, for example.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Apr 13, 2020
@jack-williams
Copy link
Collaborator

jack-williams commented Apr 13, 2020

FWIW I think the issue boils down to:

type A = Record<number,number> & [string];
type B = W[0] // string

The indexed access type ignores the index signature rather than creating an intersection type from the property and the index type. In most cases this would be what you want (but not in this very specific case).

@eyedean
Copy link
Author

eyedean commented Apr 13, 2020

Thanks for your prompt response @RyanCavanaugh and @jack-williams. :)

Currently intersections don't have any special rules to produce new types; it's not clear this would be desirable - for an intersection of N elements we'd have to do N * (N - 1) comparisons to see if any "special" rules applied, for example.

You've been in this for many more years than me, but given here N is the number of index types in the tuple (which should be manually typed, at least once), and is a compile-time, usually one-time-only operation, I really don't think the operation having a run-time complexity of O(N²) is a drawback for developers who get to here.

Thanks for the & { length: 1 }, tip!

In most cases this would be what you want ...

I didn't get this one, Jack. Record<number, number> means an object whose keys are number and the values are having the type number as well. [string] means the value for index: 0 is of type string. I read this as {0: string} since Arrays are Object in JavaScript.

Hence, what can be concluded is the first index (index 0) should have type number & string and value for the other indices should be number.

That's why to me there is only one interpretation behind this and I couldn't follow why would someone expect anything else from it.

Thanks again!

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants