TypeScript Version: 2.0.3
Code
Why I can have function type guard:
function hasValue<T>(value: T | undefined): value is T { return value !== undefined; }
but not method type guard:
export class Maybe<T> {
constructor(public value: T | undefined) {}
hasValue(): this.value is T { return this.value !== undefined; }
// ^ '{' or ';' expected.
}
?
Semi related to a random thought here in that more complicated guards could express codependency of properties:
interface Array<T> {
length: number;
pop(): this.length > 0 ? T : undefined;
}
const arr = [ 1, 2, 3 ];
if (arr.length > 0) {
const val = arr.pop(); // val is number, not number | undefined under strictNullChecks
}
This would be interesting... expressing path dependent types.
Read the OP backward... too early.
Would this also fix issues with Map?
In the case where you're using map to add memoization to functions there is a similar need to use the ! operator.
const cache = new Map<number, string[]>()
if (cache.has(n)) {
return cache.get(n)! // The if statement above should make return type `string[]` instead of `string[] | undefined`
}
What about using this type guards instead?
export class Maybe<T> {
constructor(public value: T | undefined) {}
hasValue(): this is this & { value: T } { return this.value !== undefined; }
}
///////
declare var x: Maybe<string>;
if (x.hasValue()) {
x.value.toLowerCase();
}
@mattmazzola I've used the above technique to describe a way that Map#has could work similarly, but it can get a bit messy.
Ok, I'll have to learn/explore more about the this type guards. In my Map example this type definition is pre-defined by the lib.d.ts since they're part of ES2016 I believe and I didn't want to re-write my own type which wraps the native one.
@DanielRosenwasser Any way to make that approach work for private properties?
I wanted to use this for this pattern:
https://www.typescriptlang.org/play/?ssl=14&ssc=1&pln=15&pc=1#code/KYDwDg9gTgLgBAYwDYEMDOa4BEU2ASQDs8oA3FJOAbwCg44woBLcvRCQtGKAVwRmgAKRi1zA4XFLABc2MQBoGzVuOCEAJrJxsAPnEI8kSAJTUAvjTpwmaAPKEA5hCaPBx2TAAWN65i8+AMmo4NU19Q0ozait6KGAYHihCOH80ADpQuABeHPCjKwsrGwAxFyY8Nw9vTB9UuCCqEI0tMTgo2npY+MTk1IyNOABCXIN8+kL6AVLCcuAAZW4XBzdozusAMzhBPpKyiuNTDrW4OISkuAADABIqPslYNIEF5ldjKIBaOBu+0MeIZ6WbjMFxibVBXigEAA7vpgDCAKJQSFQQQAcgAKtU5HgiCRyJQfBwnEtFAgUIRCBB4AgOKRgLAUhA4Os9uIXHiKBJFo5UcYCjQzEA
export class DateInterval {
private constructor(private start: Date, private end: Date | null) {}
isOngoing(): this is this & { end: null } {
return this.end === null
}
isFinite(): this is this & { end: Date } {
return this.end !== null
}
toFiniteString() {
if (this.isFinite()) {
return `${this.start.toString()} - ${this.end.toString()}`
}
throw new Error('This DateInterval is ongoing, cannot convert to finite interval string')
}
}
But it gives me the following error: "Property 'end' has conflicting declarations and is inaccessible in type 'DateInterval & { end: Date; }'.(2546)"
The problem with the solution by @DanielRosenwasser:
What about using
thistype guards instead?export class Maybe<T> { constructor(public value: T | undefined) {} hasValue(): this is this & { value: T } { return this.value !== undefined; } } /////// declare var x: Maybe<string>; if (x.hasValue()) { x.value.toLowerCase(); }
is that "else" doesn't work:
if (x.hasValue()) {
x.value.toLowerCase();
}
else {
x.value // x.value is string | undefined instead of undefined
}
Is there any way to negate the hasValue() function and get undefined, in this case?
Most helpful comment
Semi related to a random thought here in that more complicated guards could express codependency of properties: