Reputation: 1819
During this stayhome period I decided to dive into TypeScript and started to practice it by implementing some basic data structures. I am trying to implement a custom stack that uses custom nodes.
My StackNodes are defined like this:
class StackNode {
private val: any;
private nxt: StackNode | undefined = undefined;
constructor(val: any, nxt?: StackNode | undefined) {
this.val = val;
this.nxt = nxt || undefined;
}
get value(): any {
return this.value;
}
get next(): StackNode | undefined {
return this.next;
}
}
export default StackNode;
And the actual Stack:
class Stack {
private capacity!: number;
private top?: StackNode | undefined = undefined;
private size: number = 0;
constructor(capacity: number, initialValues?: Array<any>) {
this.capacity = capacity;
if (initialValues) {
this.size = initialValues.length;
this.top = this._initStack(initialValues, initialValues.length - 1);
}
};
private _initStack = (array: Array<any>, idx: number): StackNode => {
if (idx == 0) {
return new StackNode(array[idx], undefined);
} else {
return new StackNode(array[idx], this._initStack(array, idx-1));
}
}
pop(): any {
const value = this.top?.value();
this.top = this.top?.next();
return value;
}
}
export default Stack;
The problem here is the line with the optional chaining operator in pop-method this.top = this.top?.next()
What I have understood is that the expression this.top?.next()
should be equivalent to
(this.top === null || this.top === undefined)? undefined : this.top.next()
but I still get the error
Cannot invoke an object which is possibly 'undefined'.ts(2722)
when the call is made even though it shouldn't be undefined anymore at that stage.
Why's that? What am I missing here? Both the StackNode.nxt and Stack.top are allowed to be undefined. I have tried to do it in the old way like this:
if (this.top !== null || this.top !== undefined) {
const value = this.top.value()
this.top = this.top.next()
}
But I still get the same error, even though here it should be sure that the this.top
can not be undefined, but has to be, or at least should be, of type StackNode.
How this should work is that when popping from empty stack, pop method would return undefined and when popping the last element, its next, that is undefined, is set as the top of the stack.
I am using TS 3.8.3
Upvotes: 1
Views: 4468
Reputation: 3476
You define next as a getter, so it must be accessed like so: this.top = this.top?.next
The only reason that const value = this.top?.value();
even compiles is because you use 'any' (DONT DO THAT, EVER!!), and typescript assumes that get value
might return a function that you are invoking.
You should define StackNode using generics. For example,
class StackNode<T> {
private val: T;
private nxt: StackNode<T> | undefined = undefined;
constructor(val: T, nxt?: StackNode<T> | undefined) {
this.val = val;
this.nxt = nxt || undefined;
}
get value(): T {
return this.value;
}
get next(): StackNode<T> {
return this.next;
}
}
class Stack<T> {
private capacity!: number;
private top?: StackNode<T> | undefined = undefined;
private size: number = 0;
constructor(capacity: number, initialValues?: Array<any>) {
this.capacity = capacity;
if (initialValues) {
this.size = initialValues.length;
this.top = this._initStack(initialValues, initialValues.length - 1);
}
};
private _initStack = (array: Array<any>, idx: number): StackNode<T> => {
if (idx == 0) {
return new StackNode(array[idx], undefined);
} else {
return new StackNode(array[idx], this._initStack(array, idx-1));
}
}
pop(): T | undefined {
const value = this.top?.value(); //doesn't compile
this.top = this.top?.next(); //doesn't compile either
return value;
}
}
Then, const value = this.top?.value();
would not have compiled either.
Upvotes: 1