-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
TS fails to understand guard for Array.prototype.pop() #30406
Comments
I think is essentially the same thing as #9619, or rather, it suffers from the same limitations. |
This isn't something we're capable of tracking |
you just need an interface that represents a non-empty array and a type guard function: const arr: string[] = [];
interface NonEmptyArray<T> extends Array<T> {
// would need to implement all relevant functions.
pop: () => T;
}
function isNonEmpty<T>(arr: Array<T>): arr is NonEmptyArray<T> {
return arr.length > 0;
}
if (isNonEmpty(arr)) {
// here we are sure arr.pop() will return a value!
arr.pop().toLowerCase();
} Edit: never mind, in the general case there really isn't any way to ensure pop is valid since if you pop more than once this would still act like each one was guaranteed not to return undefined, so this doesn't get caught by typescript: if (isNonEmpty(arr)) {
arr.clear();
// typescript still under assertion that arr isn't empty!
arr.pop().toLowerCase();
} |
A simple way to let Typescript know that it can assume 'not undefined' is to use an explicit cast: if (arr.length > 0) {
(arr.pop() as string).toLowerCase();
} |
@basvanmeurs A not null assertion: if (arr.length > 0) {
arr.pop()!.toLowerCase();
} |
Is this relevant? class Stack<T> {
private data: T[] = [];
constructor() {}
push(item: T): void {
this.data.push(item);
}
pop(): T | 0 {
if (this.data.length > 0) return this.data.pop(); // Error : Type 'undefined' is not assignable to type '0 | T'.
else return 0;
}
} EDIT: below code has no problem. Weird i think. class Stack<T> {
private data: T[] = [];
constructor() {}
push(item: T): void {
this.data.push(item);
}
pop(): T {
return this.data[this.data.length]; // no error but it could actually return 'undefined'
}
} |
TypeScript already recognizes an index signature property type which must be either ‘string’ or ‘number’. In addition to that, TypeScript could keep track of if (arr.length > 0) { //guard: arr.minLength is now 1
arr.pop().toLowerCase(); //this would be fine; arr.minLength drops by 1 to 0
arr.pop().toLowerCase(); //this second call would have a type error, fixed by setting 0 to 1 in check.
} Though it is not a trivial implementation, I think Typescript would be much more useful with that enhancement. |
Related: #31376 |
TypeScript Version: 3.4.0-dev.201xxxxx
Search Terms:
Code
Expected behavior:
No error, because we tested that the array's length was greater than zero.
Actual behavior:
Error:
Object is possibly 'undefined'
Playground Link:
Note: turn on all strict checks in "options" (the link doesn't preserve these settings)
http://www.typescriptlang.org/play/index.html#src=const%20arr%3A%20string%5B%5D%20%3D%20%5B%5D%3B%0D%0A%0D%0Aif%20(Math.random()%20%3E%200.5)%20%7B%0D%0A%20%20%20%20arr.push('hi')%3B%0D%0A%7D%0D%0A%0D%0Aif%20(arr.length%20%3E%200)%20%7B%0D%0A%20%20%20%20%2F%2F%20NOTE%3A%20turn%20on%20all%20strict%20checks!%0D%0A%20%20%20%20arr.pop().toLowerCase()%3B%0D%0A%7D%0D%0A
Related Issues:
#29642 -- maybe? 🤔
The text was updated successfully, but these errors were encountered: