mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Allow filterType to consider union constraints of non-union types when determining never-ness (#43763)
* Allow filterType to consider union constraints of non-union types when determining never-ness * Move impl to callback * Baseline change in narrowing behavior into test, fix post-LKG build
This commit is contained in:
+10
-1
@@ -23819,7 +23819,16 @@ namespace ts {
|
||||
|
||||
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
|
||||
if (!assumeTrue) {
|
||||
return filterType(type, t => !isRelated(t, candidate));
|
||||
return filterType(type, t => {
|
||||
if (!isRelated(t, candidate)) {
|
||||
return true;
|
||||
}
|
||||
const constraint = getBaseConstraintOfType(t);
|
||||
if (constraint && constraint !== t) {
|
||||
return !isRelated(constraint, candidate);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// If the current type is a union type, remove all constituents that couldn't be instances of
|
||||
// the candidate type. If one or more constituents remain, return a union of those.
|
||||
|
||||
@@ -113,8 +113,8 @@ namespace ts {
|
||||
return `${newLine}${flattenDiagnosticMessageText(d.messageText, newLine)}${newLine}${newLine}`;
|
||||
}
|
||||
|
||||
export function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T {
|
||||
return !!(program as T).getState;
|
||||
export function isBuilderProgram(program: Program | BuilderProgram): program is BuilderProgram {
|
||||
return !!(program as BuilderProgram).getState;
|
||||
}
|
||||
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T, write: (s: string) => void) {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts(33,15): error TS2339: Property 'numExclusive' does not exist on type 'NumClass<number> | StrClass<string>'.
|
||||
Property 'numExclusive' does not exist on type 'StrClass<string>'.
|
||||
tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts(101,11): error TS2322: Type 'Program | T' is not assignable to type 'Program'.
|
||||
Type 'T' is not assignable to type 'Program'.
|
||||
Property 'state' is missing in type 'BuilderProgram' but required in type 'Program'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts (2 errors) ====
|
||||
class NumClass<T extends number> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public numExclusive() { }
|
||||
}
|
||||
|
||||
class StrClass<T extends string> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public strExclusive() { }
|
||||
}
|
||||
|
||||
const isNumClass = <Item extends NumClass<number> | StrClass<string>> (
|
||||
item: Item
|
||||
): item is Extract<Item, NumClass<any>> => {
|
||||
return (item instanceof NumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
class SimpleStore<Entries extends { [index: string]: NumClass<number> | StrClass<string> }> {
|
||||
private entries = { } as Entries;
|
||||
|
||||
public get<EntryId extends keyof Entries>(entryId: EntryId): Entries[EntryId] {
|
||||
let entry = this.entries[entryId];
|
||||
|
||||
entry.numExclusive(); // error - expected.
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS2339: Property 'numExclusive' does not exist on type 'NumClass<number> | StrClass<string>'.
|
||||
!!! error TS2339: Property 'numExclusive' does not exist on type 'StrClass<string>'.
|
||||
|
||||
if (isNumClass(entry)) {
|
||||
entry.numExclusive(); // works
|
||||
return entry;
|
||||
}
|
||||
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
}
|
||||
}
|
||||
|
||||
type Slice = {
|
||||
[index: string]: NumClass<number> | StrClass<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
class ComplexStore<Slices extends { [index: string]: Slice }> {
|
||||
private slices = { } as Slices;
|
||||
|
||||
public get<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
}
|
||||
|
||||
item.get();
|
||||
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
|
||||
return item; // type is Extract ...
|
||||
}
|
||||
|
||||
public get2<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
return item;
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
|
||||
item.get();
|
||||
|
||||
return item; // type is never
|
||||
}
|
||||
}
|
||||
|
||||
// from the compiler itself
|
||||
interface BuilderProgram {
|
||||
getProgram(): Program;
|
||||
}
|
||||
interface Program {
|
||||
state: any;
|
||||
}
|
||||
declare function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T;
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T) {
|
||||
const x: Program = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
~
|
||||
!!! error TS2322: Type 'Program | T' is not assignable to type 'Program'.
|
||||
!!! error TS2322: Type 'T' is not assignable to type 'Program'.
|
||||
!!! error TS2322: Property 'state' is missing in type 'BuilderProgram' but required in type 'Program'.
|
||||
!!! related TS2728 tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts:97:5: 'state' is declared here.
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//// [quickinfoTypeAtReturnPositionsInaccurate.ts]
|
||||
class NumClass<T extends number> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public numExclusive() { }
|
||||
}
|
||||
|
||||
class StrClass<T extends string> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public strExclusive() { }
|
||||
}
|
||||
|
||||
const isNumClass = <Item extends NumClass<number> | StrClass<string>> (
|
||||
item: Item
|
||||
): item is Extract<Item, NumClass<any>> => {
|
||||
return (item instanceof NumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
class SimpleStore<Entries extends { [index: string]: NumClass<number> | StrClass<string> }> {
|
||||
private entries = { } as Entries;
|
||||
|
||||
public get<EntryId extends keyof Entries>(entryId: EntryId): Entries[EntryId] {
|
||||
let entry = this.entries[entryId];
|
||||
|
||||
entry.numExclusive(); // error - expected.
|
||||
|
||||
if (isNumClass(entry)) {
|
||||
entry.numExclusive(); // works
|
||||
return entry;
|
||||
}
|
||||
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
}
|
||||
}
|
||||
|
||||
type Slice = {
|
||||
[index: string]: NumClass<number> | StrClass<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
class ComplexStore<Slices extends { [index: string]: Slice }> {
|
||||
private slices = { } as Slices;
|
||||
|
||||
public get<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
}
|
||||
|
||||
item.get();
|
||||
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
|
||||
return item; // type is Extract ...
|
||||
}
|
||||
|
||||
public get2<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
return item;
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
|
||||
item.get();
|
||||
|
||||
return item; // type is never
|
||||
}
|
||||
}
|
||||
|
||||
// from the compiler itself
|
||||
interface BuilderProgram {
|
||||
getProgram(): Program;
|
||||
}
|
||||
interface Program {
|
||||
state: any;
|
||||
}
|
||||
declare function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T;
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T) {
|
||||
const x: Program = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
}
|
||||
|
||||
//// [quickinfoTypeAtReturnPositionsInaccurate.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
exports.listFiles = void 0;
|
||||
var NumClass = /** @class */ (function () {
|
||||
function NumClass() {
|
||||
}
|
||||
NumClass.prototype.get = function () {
|
||||
return this.value;
|
||||
};
|
||||
NumClass.prototype.numExclusive = function () { };
|
||||
return NumClass;
|
||||
}());
|
||||
var StrClass = /** @class */ (function () {
|
||||
function StrClass() {
|
||||
}
|
||||
StrClass.prototype.get = function () {
|
||||
return this.value;
|
||||
};
|
||||
StrClass.prototype.strExclusive = function () { };
|
||||
return StrClass;
|
||||
}());
|
||||
var isNumClass = function (item) {
|
||||
return (item instanceof NumClass);
|
||||
};
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
var SimpleStore = /** @class */ (function () {
|
||||
function SimpleStore() {
|
||||
this.entries = {};
|
||||
}
|
||||
SimpleStore.prototype.get = function (entryId) {
|
||||
var entry = this.entries[entryId];
|
||||
entry.numExclusive(); // error - expected.
|
||||
if (isNumClass(entry)) {
|
||||
entry.numExclusive(); // works
|
||||
return entry;
|
||||
}
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
};
|
||||
return SimpleStore;
|
||||
}());
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
var ComplexStore = /** @class */ (function () {
|
||||
function ComplexStore() {
|
||||
this.slices = {};
|
||||
}
|
||||
ComplexStore.prototype.get = function (sliceId, sliceKey) {
|
||||
var item = this.slices[sliceId][sliceKey];
|
||||
if (isNumClass(item)) {
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
}
|
||||
item.get();
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
return item; // type is Extract ...
|
||||
};
|
||||
ComplexStore.prototype.get2 = function (sliceId, sliceKey) {
|
||||
var item = this.slices[sliceId][sliceKey];
|
||||
if (isNumClass(item)) {
|
||||
return item;
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
item.get();
|
||||
return item; // type is never
|
||||
};
|
||||
return ComplexStore;
|
||||
}());
|
||||
function listFiles(program) {
|
||||
var x = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
}
|
||||
exports.listFiles = listFiles;
|
||||
@@ -0,0 +1,280 @@
|
||||
=== tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts ===
|
||||
class NumClass<T extends number> {
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 15))
|
||||
|
||||
private value!: T;
|
||||
>value : Symbol(NumClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 34))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 15))
|
||||
|
||||
public get(): T {
|
||||
>get : Symbol(NumClass.get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 1, 22))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 15))
|
||||
|
||||
return this.value;
|
||||
>this.value : Symbol(NumClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 34))
|
||||
>this : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
>value : Symbol(NumClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 34))
|
||||
}
|
||||
public numExclusive() { }
|
||||
>numExclusive : Symbol(NumClass.numExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 4, 5))
|
||||
}
|
||||
|
||||
class StrClass<T extends string> {
|
||||
>StrClass : Symbol(StrClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 6, 1))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 15))
|
||||
|
||||
private value!: T;
|
||||
>value : Symbol(StrClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 34))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 15))
|
||||
|
||||
public get(): T {
|
||||
>get : Symbol(StrClass.get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 9, 22))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 15))
|
||||
|
||||
return this.value;
|
||||
>this.value : Symbol(StrClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 34))
|
||||
>this : Symbol(StrClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 6, 1))
|
||||
>value : Symbol(StrClass.value, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 8, 34))
|
||||
}
|
||||
public strExclusive() { }
|
||||
>strExclusive : Symbol(StrClass.strExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 12, 5))
|
||||
}
|
||||
|
||||
const isNumClass = <Item extends NumClass<number> | StrClass<string>> (
|
||||
>isNumClass : Symbol(isNumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 5))
|
||||
>Item : Symbol(Item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 20))
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
>StrClass : Symbol(StrClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 6, 1))
|
||||
|
||||
item: Item
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 71))
|
||||
>Item : Symbol(Item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 20))
|
||||
|
||||
): item is Extract<Item, NumClass<any>> => {
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 71))
|
||||
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
|
||||
>Item : Symbol(Item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 20))
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
|
||||
return (item instanceof NumClass);
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 71))
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
}
|
||||
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
class SimpleStore<Entries extends { [index: string]: NumClass<number> | StrClass<string> }> {
|
||||
>SimpleStore : Symbol(SimpleStore, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 20, 5))
|
||||
>Entries : Symbol(Entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 18))
|
||||
>index : Symbol(index, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 37))
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
>StrClass : Symbol(StrClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 6, 1))
|
||||
|
||||
private entries = { } as Entries;
|
||||
>entries : Symbol(SimpleStore.entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 93))
|
||||
>Entries : Symbol(Entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 18))
|
||||
|
||||
public get<EntryId extends keyof Entries>(entryId: EntryId): Entries[EntryId] {
|
||||
>get : Symbol(SimpleStore.get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 27, 37))
|
||||
>EntryId : Symbol(EntryId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 29, 15))
|
||||
>Entries : Symbol(Entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 18))
|
||||
>entryId : Symbol(entryId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 29, 46))
|
||||
>EntryId : Symbol(EntryId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 29, 15))
|
||||
>Entries : Symbol(Entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 18))
|
||||
>EntryId : Symbol(EntryId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 29, 15))
|
||||
|
||||
let entry = this.entries[entryId];
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
>this.entries : Symbol(SimpleStore.entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 93))
|
||||
>this : Symbol(SimpleStore, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 20, 5))
|
||||
>entries : Symbol(SimpleStore.entries, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 26, 93))
|
||||
>entryId : Symbol(entryId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 29, 46))
|
||||
|
||||
entry.numExclusive(); // error - expected.
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
|
||||
if (isNumClass(entry)) {
|
||||
>isNumClass : Symbol(isNumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 5))
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
|
||||
entry.numExclusive(); // works
|
||||
>entry.numExclusive : Symbol(NumClass.numExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 4, 5))
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
>numExclusive : Symbol(NumClass.numExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 4, 5))
|
||||
|
||||
return entry;
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
}
|
||||
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
>entry : Symbol(entry, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 30, 11))
|
||||
}
|
||||
}
|
||||
|
||||
type Slice = {
|
||||
>Slice : Symbol(Slice, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 41, 1))
|
||||
|
||||
[index: string]: NumClass<number> | StrClass<string>
|
||||
>index : Symbol(index, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 44, 5))
|
||||
>NumClass : Symbol(NumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 0, 0))
|
||||
>StrClass : Symbol(StrClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 6, 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
class ComplexStore<Slices extends { [index: string]: Slice }> {
|
||||
>ComplexStore : Symbol(ComplexStore, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 45, 1))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>index : Symbol(index, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 37))
|
||||
>Slice : Symbol(Slice, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 41, 1))
|
||||
|
||||
private slices = { } as Slices;
|
||||
>slices : Symbol(ComplexStore.slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 63))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
|
||||
public get<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
>get : Symbol(ComplexStore.get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 55, 35))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 15))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 44))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 15))
|
||||
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
>sliceId : Symbol(sliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 85))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 15))
|
||||
>sliceKey : Symbol(sliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 58, 25))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 44))
|
||||
|
||||
): Slices[SliceId][SliceKey] {
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 15))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 44))
|
||||
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 60, 11))
|
||||
>this.slices : Symbol(ComplexStore.slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 63))
|
||||
>this : Symbol(ComplexStore, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 45, 1))
|
||||
>slices : Symbol(ComplexStore.slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 63))
|
||||
>sliceId : Symbol(sliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 57, 85))
|
||||
>sliceKey : Symbol(sliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 58, 25))
|
||||
|
||||
if (isNumClass(item)) {
|
||||
>isNumClass : Symbol(isNumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 5))
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 60, 11))
|
||||
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
>item.numExclusive : Symbol(NumClass.numExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 4, 5))
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 60, 11))
|
||||
>numExclusive : Symbol(NumClass.numExclusive, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 4, 5))
|
||||
}
|
||||
|
||||
item.get();
|
||||
>item.get : Symbol(get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 1, 22), Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 9, 22))
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 60, 11))
|
||||
>get : Symbol(get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 1, 22), Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 9, 22))
|
||||
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
|
||||
return item; // type is Extract ...
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 60, 11))
|
||||
}
|
||||
|
||||
public get2<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
>get2 : Symbol(ComplexStore.get2, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 72, 5))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 16))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 45))
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 16))
|
||||
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
>sliceId : Symbol(sliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 86))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 16))
|
||||
>sliceKey : Symbol(sliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 75, 25))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 45))
|
||||
|
||||
): Slices[SliceId][SliceKey] {
|
||||
>Slices : Symbol(Slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 19))
|
||||
>SliceId : Symbol(SliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 16))
|
||||
>SliceKey : Symbol(SliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 45))
|
||||
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 77, 11))
|
||||
>this.slices : Symbol(ComplexStore.slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 63))
|
||||
>this : Symbol(ComplexStore, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 45, 1))
|
||||
>slices : Symbol(ComplexStore.slices, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 54, 63))
|
||||
>sliceId : Symbol(sliceId, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 74, 86))
|
||||
>sliceKey : Symbol(sliceKey, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 75, 25))
|
||||
|
||||
if (isNumClass(item)) {
|
||||
>isNumClass : Symbol(isNumClass, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 16, 5))
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 77, 11))
|
||||
|
||||
return item;
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 77, 11))
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
|
||||
item.get();
|
||||
>item.get : Symbol(get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 1, 22), Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 9, 22))
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 77, 11))
|
||||
>get : Symbol(get, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 1, 22), Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 9, 22))
|
||||
|
||||
return item; // type is never
|
||||
>item : Symbol(item, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 77, 11))
|
||||
}
|
||||
}
|
||||
|
||||
// from the compiler itself
|
||||
interface BuilderProgram {
|
||||
>BuilderProgram : Symbol(BuilderProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 89, 1))
|
||||
|
||||
getProgram(): Program;
|
||||
>getProgram : Symbol(BuilderProgram.getProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 92, 26))
|
||||
>Program : Symbol(Program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 94, 1))
|
||||
}
|
||||
interface Program {
|
||||
>Program : Symbol(Program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 94, 1))
|
||||
|
||||
state: any;
|
||||
>state : Symbol(Program.state, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 95, 19))
|
||||
}
|
||||
declare function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T;
|
||||
>isBuilderProgram : Symbol(isBuilderProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 97, 1))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 34))
|
||||
>BuilderProgram : Symbol(BuilderProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 89, 1))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 60))
|
||||
>Program : Symbol(Program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 94, 1))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 34))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 60))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 34))
|
||||
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T) {
|
||||
>listFiles : Symbol(listFiles, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 98, 96))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 26))
|
||||
>BuilderProgram : Symbol(BuilderProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 89, 1))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 52))
|
||||
>Program : Symbol(Program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 94, 1))
|
||||
>T : Symbol(T, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 26))
|
||||
|
||||
const x: Program = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
>x : Symbol(x, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 100, 9))
|
||||
>Program : Symbol(Program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 94, 1))
|
||||
>isBuilderProgram : Symbol(isBuilderProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 97, 1))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 52))
|
||||
>program.getProgram : Symbol(BuilderProgram.getProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 92, 26))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 52))
|
||||
>getProgram : Symbol(BuilderProgram.getProgram, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 92, 26))
|
||||
>program : Symbol(program, Decl(quickinfoTypeAtReturnPositionsInaccurate.ts, 99, 52))
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
=== tests/cases/compiler/quickinfoTypeAtReturnPositionsInaccurate.ts ===
|
||||
class NumClass<T extends number> {
|
||||
>NumClass : NumClass<T>
|
||||
|
||||
private value!: T;
|
||||
>value : T
|
||||
|
||||
public get(): T {
|
||||
>get : () => T
|
||||
|
||||
return this.value;
|
||||
>this.value : T
|
||||
>this : this
|
||||
>value : T
|
||||
}
|
||||
public numExclusive() { }
|
||||
>numExclusive : () => void
|
||||
}
|
||||
|
||||
class StrClass<T extends string> {
|
||||
>StrClass : StrClass<T>
|
||||
|
||||
private value!: T;
|
||||
>value : T
|
||||
|
||||
public get(): T {
|
||||
>get : () => T
|
||||
|
||||
return this.value;
|
||||
>this.value : T
|
||||
>this : this
|
||||
>value : T
|
||||
}
|
||||
public strExclusive() { }
|
||||
>strExclusive : () => void
|
||||
}
|
||||
|
||||
const isNumClass = <Item extends NumClass<number> | StrClass<string>> (
|
||||
>isNumClass : <Item extends NumClass<number> | StrClass<string>>(item: Item) => item is Extract<Item, NumClass<any>>
|
||||
><Item extends NumClass<number> | StrClass<string>> ( item: Item ): item is Extract<Item, NumClass<any>> => { return (item instanceof NumClass); } : <Item extends NumClass<number> | StrClass<string>>(item: Item) => item is Extract<Item, NumClass<any>>
|
||||
|
||||
item: Item
|
||||
>item : Item
|
||||
|
||||
): item is Extract<Item, NumClass<any>> => {
|
||||
return (item instanceof NumClass);
|
||||
>(item instanceof NumClass) : boolean
|
||||
>item instanceof NumClass : boolean
|
||||
>item : Item
|
||||
>NumClass : typeof NumClass
|
||||
}
|
||||
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
class SimpleStore<Entries extends { [index: string]: NumClass<number> | StrClass<string> }> {
|
||||
>SimpleStore : SimpleStore<Entries>
|
||||
>index : string
|
||||
|
||||
private entries = { } as Entries;
|
||||
>entries : Entries
|
||||
>{ } as Entries : Entries
|
||||
>{ } : {}
|
||||
|
||||
public get<EntryId extends keyof Entries>(entryId: EntryId): Entries[EntryId] {
|
||||
>get : <EntryId extends keyof Entries>(entryId: EntryId) => Entries[EntryId]
|
||||
>entryId : EntryId
|
||||
|
||||
let entry = this.entries[entryId];
|
||||
>entry : Entries[EntryId]
|
||||
>this.entries[entryId] : Entries[EntryId]
|
||||
>this.entries : Entries
|
||||
>this : this
|
||||
>entries : Entries
|
||||
>entryId : EntryId
|
||||
|
||||
entry.numExclusive(); // error - expected.
|
||||
>entry.numExclusive() : any
|
||||
>entry.numExclusive : any
|
||||
>entry : NumClass<number> | StrClass<string>
|
||||
>numExclusive : any
|
||||
|
||||
if (isNumClass(entry)) {
|
||||
>isNumClass(entry) : boolean
|
||||
>isNumClass : <Item extends NumClass<number> | StrClass<string>>(item: Item) => item is Extract<Item, NumClass<any>>
|
||||
>entry : Entries[EntryId]
|
||||
|
||||
entry.numExclusive(); // works
|
||||
>entry.numExclusive() : void
|
||||
>entry.numExclusive : () => void
|
||||
>entry : Extract<Entries[EntryId], NumClass<any>>
|
||||
>numExclusive : () => void
|
||||
|
||||
return entry;
|
||||
>entry : Extract<Entries[EntryId], NumClass<any>>
|
||||
}
|
||||
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
>entry : Entries[EntryId]
|
||||
}
|
||||
}
|
||||
|
||||
type Slice = {
|
||||
>Slice : Slice
|
||||
|
||||
[index: string]: NumClass<number> | StrClass<string>
|
||||
>index : string
|
||||
}
|
||||
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
class ComplexStore<Slices extends { [index: string]: Slice }> {
|
||||
>ComplexStore : ComplexStore<Slices>
|
||||
>index : string
|
||||
|
||||
private slices = { } as Slices;
|
||||
>slices : Slices
|
||||
>{ } as Slices : Slices
|
||||
>{ } : {}
|
||||
|
||||
public get<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
>get : <SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(sliceId: SliceId, sliceKey: SliceKey) => Slices[SliceId][SliceKey]
|
||||
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
>sliceId : SliceId
|
||||
>sliceKey : SliceKey
|
||||
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
>this.slices[sliceId][sliceKey] : Slices[SliceId][SliceKey]
|
||||
>this.slices[sliceId] : Slices[SliceId]
|
||||
>this.slices : Slices
|
||||
>this : this
|
||||
>slices : Slices
|
||||
>sliceId : SliceId
|
||||
>sliceKey : SliceKey
|
||||
|
||||
if (isNumClass(item)) {
|
||||
>isNumClass(item) : boolean
|
||||
>isNumClass : <Item extends NumClass<number> | StrClass<string>>(item: Item) => item is Extract<Item, NumClass<any>>
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
>item.numExclusive() : void
|
||||
>item.numExclusive : () => void
|
||||
>item : Extract<Slices[SliceId][SliceKey], NumClass<any>>
|
||||
>numExclusive : () => void
|
||||
}
|
||||
|
||||
item.get();
|
||||
>item.get() : string | number
|
||||
>item.get : (() => number) | (() => string)
|
||||
>item : NumClass<number> | StrClass<string>
|
||||
>get : (() => number) | (() => string)
|
||||
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
|
||||
return item; // type is Extract ...
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
}
|
||||
|
||||
public get2<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
>get2 : <SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(sliceId: SliceId, sliceKey: SliceKey) => Slices[SliceId][SliceKey]
|
||||
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
>sliceId : SliceId
|
||||
>sliceKey : SliceKey
|
||||
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
>this.slices[sliceId][sliceKey] : Slices[SliceId][SliceKey]
|
||||
>this.slices[sliceId] : Slices[SliceId]
|
||||
>this.slices : Slices
|
||||
>this : this
|
||||
>slices : Slices
|
||||
>sliceId : SliceId
|
||||
>sliceKey : SliceKey
|
||||
|
||||
if (isNumClass(item)) {
|
||||
>isNumClass(item) : boolean
|
||||
>isNumClass : <Item extends NumClass<number> | StrClass<string>>(item: Item) => item is Extract<Item, NumClass<any>>
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
|
||||
return item;
|
||||
>item : Extract<Slices[SliceId][SliceKey], NumClass<any>>
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
|
||||
item.get();
|
||||
>item.get() : string | number
|
||||
>item.get : (() => number) | (() => string)
|
||||
>item : NumClass<number> | StrClass<string>
|
||||
>get : (() => number) | (() => string)
|
||||
|
||||
return item; // type is never
|
||||
>item : Slices[SliceId][SliceKey]
|
||||
}
|
||||
}
|
||||
|
||||
// from the compiler itself
|
||||
interface BuilderProgram {
|
||||
getProgram(): Program;
|
||||
>getProgram : () => Program
|
||||
}
|
||||
interface Program {
|
||||
state: any;
|
||||
>state : any
|
||||
}
|
||||
declare function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T;
|
||||
>isBuilderProgram : <T extends BuilderProgram>(program: Program | T) => program is T
|
||||
>program : Program | T
|
||||
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T) {
|
||||
>listFiles : <T extends BuilderProgram>(program: Program | T) => void
|
||||
>program : Program | T
|
||||
|
||||
const x: Program = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
>x : Program
|
||||
>isBuilderProgram(program) ? program.getProgram() : program : Program | T
|
||||
>isBuilderProgram(program) : boolean
|
||||
>isBuilderProgram : <T extends BuilderProgram>(program: Program | T) => program is T
|
||||
>program : Program | T
|
||||
>program.getProgram() : Program
|
||||
>program.getProgram : () => Program
|
||||
>program : T
|
||||
>getProgram : () => Program
|
||||
>program : Program | T
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
class NumClass<T extends number> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public numExclusive() { }
|
||||
}
|
||||
|
||||
class StrClass<T extends string> {
|
||||
private value!: T;
|
||||
public get(): T {
|
||||
return this.value;
|
||||
}
|
||||
public strExclusive() { }
|
||||
}
|
||||
|
||||
const isNumClass = <Item extends NumClass<number> | StrClass<string>> (
|
||||
item: Item
|
||||
): item is Extract<Item, NumClass<any>> => {
|
||||
return (item instanceof NumClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* An example with one dimensional dictionary. Everything worked ok here, even in prior
|
||||
* versions.
|
||||
*/
|
||||
class SimpleStore<Entries extends { [index: string]: NumClass<number> | StrClass<string> }> {
|
||||
private entries = { } as Entries;
|
||||
|
||||
public get<EntryId extends keyof Entries>(entryId: EntryId): Entries[EntryId] {
|
||||
let entry = this.entries[entryId];
|
||||
|
||||
entry.numExclusive(); // error - expected.
|
||||
|
||||
if (isNumClass(entry)) {
|
||||
entry.numExclusive(); // works
|
||||
return entry;
|
||||
}
|
||||
|
||||
return entry; // type is Entries[EntryId] - all fine
|
||||
}
|
||||
}
|
||||
|
||||
type Slice = {
|
||||
[index: string]: NumClass<number> | StrClass<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* A an example with 2-dimensional dictionary.
|
||||
*
|
||||
* In v4.1 the `isNumClass` type guard doesn't work at all.
|
||||
* In v4.2 or later, `isNumClass` type guard leaks outside its
|
||||
* scope.
|
||||
*/
|
||||
class ComplexStore<Slices extends { [index: string]: Slice }> {
|
||||
private slices = { } as Slices;
|
||||
|
||||
public get<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
item.numExclusive(); // works only since version 4.2
|
||||
}
|
||||
|
||||
item.get();
|
||||
|
||||
// unfortunately, doesn't work completely.
|
||||
// it seems like item's predicated type leaks outside the bracket...
|
||||
|
||||
return item; // type is Extract ...
|
||||
}
|
||||
|
||||
public get2<SliceId extends keyof Slices, SliceKey extends keyof Slices[SliceId]>(
|
||||
sliceId: SliceId, sliceKey: SliceKey
|
||||
): Slices[SliceId][SliceKey] {
|
||||
let item = this.slices[sliceId][sliceKey];
|
||||
|
||||
if (isNumClass(item)) {
|
||||
return item;
|
||||
}
|
||||
// it seems like the compiler asumes the above condition is always
|
||||
// truthy
|
||||
|
||||
item.get();
|
||||
|
||||
return item; // type is never
|
||||
}
|
||||
}
|
||||
|
||||
// from the compiler itself
|
||||
interface BuilderProgram {
|
||||
getProgram(): Program;
|
||||
}
|
||||
interface Program {
|
||||
state: any;
|
||||
}
|
||||
declare function isBuilderProgram<T extends BuilderProgram>(program: Program | T): program is T;
|
||||
export function listFiles<T extends BuilderProgram>(program: Program | T) {
|
||||
const x: Program = isBuilderProgram(program) ? program.getProgram() : program;
|
||||
}
|
||||
Reference in New Issue
Block a user