I'm having some trouble with an iterable object (if my understanding is correct) in Typescript strict mode (strict null checks). I want to use the returned object of 'String.prototype.match()'.
const matchLetter: RegExpMatchArray | null = points[0].match(/[a-zA-Z]/);
const direction: Tdirection = matchLetter[0];
// Two errors:
// 1. Object is possibly 'null'. ts(2531)
// 2. Type 'string' is not assignable to type 'Tdirection'. ts(2322)
// don't change RegExpMatchArray, it's from typescript lib.es5.d.ts
interface RegExpMatchArray extends Array<string> { index?: number; input?: string; }
// custom types
type Tlocation = { x: number; y: number };
type Tdirection = "R" | "U" | "L" | "D";
// demo data
const pathToPlot = [["R0", "R1", "R2"],["U0", "U1"],["L0"],["D0"]];
// demo operations
const operations = {
R: (index: number, lastLocation: Tlocation) => { return { x: lastLocation.x + index, y: lastLocation.y }},
U: (index: number, lastLocation: Tlocation) => { return { x: lastLocation.x + index, y: lastLocation.y }},
L: (index: number, lastLocation: Tlocation) => { return { x: lastLocation.x - index, y: lastLocation.y }},
D: (index: number, lastLocation: Tlocation) => { return { x: lastLocation.x, y: lastLocation.y - index }}
};
pathToPlot.forEach(points => {
// In JS I did it like this:
// const direction = points[0].match(/[a-zA-Z]/)[0];
// Typescript equivalent?
const matchLetter: RegExpMatchArray | null = points[0].match(/[a-zA-Z]/);
// This is giving errors:
const direction: Tdirection = matchLetter[0];
// two errors:
// 1. Object is possibly 'null'. ts(2531)
// 2. Type 'string' is not assignable to type 'Tdirection'. ts(2322)
console.log(operations[direction](1, { x: 0, y: 0 }));
});
Live code example: https://codesandbox.io/s/reverent-thunder-s2wzn
I've read a couple of articles on this matter.
A suggestion was to define a new type for the RegExpArray (described here). But that seems odd to re-define an existing type. I'd rather use the existing one and perform my own logic around it to make it pass.
I also read the article "How to avoid null check pollution in Javascript: use Optionals" (article). It states that you can use a library which provides a method to check if the value is null. If so it handles it with an error message and then it returns something else.
Isn't there a way without the use of a library?
You have to check for null first:
const matchLetter: RegExpMatchArray | null = points[0].match(/[a-zA-Z]/);
if (matchLetter) {
const direction: Tdirection = matchLetter[0];
}
If Typescript does not automatically recognize that the matchLetter
value is already checked then make it explicit:
const matchLetter: RegExpMatchArray | null = points[0].match(/[a-zA-Z]/);
if (matchLetter) {
const direction: Tdirection = matchLetter![0] as Tdirection;
}
The trailing !
is a socalled non-null-assertion-operator which makes it explicit that a nullable variable will contain a value at this point. Typescript should not require that if we have the type guard before the access to matchLetter
. But I have seen cases where the Typescript linter still complained.
The error on direction
is clear since you are trying to assign a generic string to a string enumeration. I have changed the code above to use an as
cast to silence the linter.
Once you changed the direction
assignment, you will also need a change in the operations
expression at the end:
var direction: Tdirection | undefined;
if (matchLetter) {
direction = matchLetter[0] as Tdirection;
}
if (direction) {
console.log(operations[direction](1, { x: 0, y: 0 }));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With