mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-14 18:39:19 -05:00
Merge pull request #12251 from Microsoft/fixTypePredicateStructuralMatch
Fix type predicates with structurally identical types
This commit is contained in:
@@ -9741,20 +9741,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (targetType) {
|
||||
return getNarrowedType(type, targetType, assumeTrue);
|
||||
return getNarrowedType(type, targetType, assumeTrue, isTypeInstanceOf);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) {
|
||||
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
|
||||
if (!assumeTrue) {
|
||||
return filterType(type, t => !isTypeInstanceOf(t, candidate));
|
||||
return filterType(type, t => !isRelated(t, candidate));
|
||||
}
|
||||
// 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.
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
const assignableType = filterType(type, t => isTypeInstanceOf(t, candidate));
|
||||
const assignableType = filterType(type, t => isRelated(t, candidate));
|
||||
if (!(assignableType.flags & TypeFlags.Never)) {
|
||||
return assignableType;
|
||||
}
|
||||
@@ -9790,7 +9790,7 @@ namespace ts {
|
||||
const predicateArgument = callExpression.arguments[predicate.parameterIndex];
|
||||
if (predicateArgument) {
|
||||
if (isMatchingReference(reference, predicateArgument)) {
|
||||
return getNarrowedType(type, predicate.type, assumeTrue);
|
||||
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
|
||||
}
|
||||
if (containsMatchingReference(reference, predicateArgument)) {
|
||||
return declaredType;
|
||||
@@ -9803,7 +9803,7 @@ namespace ts {
|
||||
const accessExpression = invokedExpression as ElementAccessExpression | PropertyAccessExpression;
|
||||
const possibleReference = skipParentheses(accessExpression.expression);
|
||||
if (isMatchingReference(reference, possibleReference)) {
|
||||
return getNarrowedType(type, predicate.type, assumeTrue);
|
||||
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
|
||||
}
|
||||
if (containsMatchingReference(reference, possibleReference)) {
|
||||
return declaredType;
|
||||
|
||||
@@ -64,25 +64,13 @@ if (isNodeList(sourceObj)) {
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
|
||||
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
|
||||
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
|
||||
}
|
||||
|
||||
if (isHTMLCollection(sourceObj)) {
|
||||
>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
|
||||
}
|
||||
|
||||
if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
|
||||
>isNodeList : Symbol(isNodeList, Decl(controlFlowBinaryOrExpression.ts, 16, 1))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
|
||||
sourceObj.length;
|
||||
@@ -91,3 +79,15 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
|
||||
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
|
||||
}
|
||||
|
||||
if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
|
||||
>isNodeList : Symbol(isNodeList, Decl(controlFlowBinaryOrExpression.ts, 16, 1))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
>isHTMLCollection : Symbol(isHTMLCollection, Decl(controlFlowBinaryOrExpression.ts, 18, 67))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
|
||||
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
|
||||
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ if (isNodeList(sourceObj)) {
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : number
|
||||
>sourceObj : NodeList
|
||||
>sourceObj : NodeList | HTMLCollection
|
||||
>length : number
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ if (isHTMLCollection(sourceObj)) {
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : number
|
||||
>sourceObj : HTMLCollection
|
||||
>sourceObj : NodeList | HTMLCollection
|
||||
>length : number
|
||||
}
|
||||
|
||||
@@ -102,11 +102,11 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
|
||||
>sourceObj : NodeList | HTMLCollection | { a: string; }
|
||||
>isHTMLCollection(sourceObj) : boolean
|
||||
>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection
|
||||
>sourceObj : HTMLCollection | { a: string; }
|
||||
>sourceObj : { a: string; }
|
||||
|
||||
sourceObj.length;
|
||||
>sourceObj.length : number
|
||||
>sourceObj : NodeList | HTMLCollection
|
||||
>sourceObj : NodeList
|
||||
>length : number
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts(32,18): error TS2339: Property 'item' does not exist on type 'never'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts (1 errors) ====
|
||||
// Repro from #7271
|
||||
|
||||
class C1 { item: string }
|
||||
class C2 { item: string[] }
|
||||
class C3 { item: string }
|
||||
|
||||
function foo1(x: C1 | C2 | C3): string {
|
||||
if (x instanceof C1) {
|
||||
return x.item;
|
||||
}
|
||||
else if (x instanceof C2) {
|
||||
return x.item[0];
|
||||
}
|
||||
else if (x instanceof C3) {
|
||||
return x.item;
|
||||
}
|
||||
return "error";
|
||||
}
|
||||
|
||||
function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 }
|
||||
function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 }
|
||||
function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 }
|
||||
|
||||
function foo2(x: C1 | C2 | C3): string {
|
||||
if (isC1(x)) {
|
||||
return x.item;
|
||||
}
|
||||
else if (isC2(x)) {
|
||||
return x.item[0];
|
||||
}
|
||||
else if (isC3(x)) {
|
||||
return x.item;
|
||||
~~~~
|
||||
!!! error TS2339: Property 'item' does not exist on type 'never'.
|
||||
}
|
||||
return "error";
|
||||
}
|
||||
|
||||
// More tests
|
||||
|
||||
class A { a: string }
|
||||
class A1 extends A { }
|
||||
class A2 { a: string }
|
||||
class B extends A { b: string }
|
||||
|
||||
function goo(x: A) {
|
||||
if (x instanceof A) {
|
||||
x; // A
|
||||
}
|
||||
else {
|
||||
x; // never
|
||||
}
|
||||
if (x instanceof A1) {
|
||||
x; // A1
|
||||
}
|
||||
else {
|
||||
x; // A
|
||||
}
|
||||
if (x instanceof A2) {
|
||||
x; // A2
|
||||
}
|
||||
else {
|
||||
x; // A
|
||||
}
|
||||
if (x instanceof B) {
|
||||
x; // B
|
||||
}
|
||||
else {
|
||||
x; // A
|
||||
}
|
||||
}
|
||||
|
||||
46
tests/baselines/reference/typePredicateStructuralMatch.js
Normal file
46
tests/baselines/reference/typePredicateStructuralMatch.js
Normal file
@@ -0,0 +1,46 @@
|
||||
//// [typePredicateStructuralMatch.ts]
|
||||
// Repro from #12235
|
||||
|
||||
getResults1([]);
|
||||
getResults1({data: []});
|
||||
|
||||
getResults2([]);
|
||||
getResults2({data: []});
|
||||
|
||||
type Result = { value: string };
|
||||
type Results = Result[];
|
||||
|
||||
function isResponseInData<T>(value: T | { data: T}): value is { data: T } {
|
||||
return value.hasOwnProperty('data');
|
||||
}
|
||||
|
||||
function getResults1(value: Results | { data: Results }): Results {
|
||||
return isResponseInData(value) ? value.data : value;
|
||||
}
|
||||
|
||||
function isPlainResponse<T>(value: T | { data: T}): value is T {
|
||||
return !value.hasOwnProperty('data');
|
||||
}
|
||||
|
||||
function getResults2(value: Results | { data: Results }): Results {
|
||||
return isPlainResponse(value) ? value : value.data;
|
||||
}
|
||||
|
||||
//// [typePredicateStructuralMatch.js]
|
||||
// Repro from #12235
|
||||
getResults1([]);
|
||||
getResults1({ data: [] });
|
||||
getResults2([]);
|
||||
getResults2({ data: [] });
|
||||
function isResponseInData(value) {
|
||||
return value.hasOwnProperty('data');
|
||||
}
|
||||
function getResults1(value) {
|
||||
return isResponseInData(value) ? value.data : value;
|
||||
}
|
||||
function isPlainResponse(value) {
|
||||
return !value.hasOwnProperty('data');
|
||||
}
|
||||
function getResults2(value) {
|
||||
return isPlainResponse(value) ? value : value.data;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
=== tests/cases/compiler/typePredicateStructuralMatch.ts ===
|
||||
// Repro from #12235
|
||||
|
||||
getResults1([]);
|
||||
>getResults1 : Symbol(getResults1, Decl(typePredicateStructuralMatch.ts, 13, 1))
|
||||
|
||||
getResults1({data: []});
|
||||
>getResults1 : Symbol(getResults1, Decl(typePredicateStructuralMatch.ts, 13, 1))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 3, 13))
|
||||
|
||||
getResults2([]);
|
||||
>getResults2 : Symbol(getResults2, Decl(typePredicateStructuralMatch.ts, 21, 1))
|
||||
|
||||
getResults2({data: []});
|
||||
>getResults2 : Symbol(getResults2, Decl(typePredicateStructuralMatch.ts, 21, 1))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 6, 13))
|
||||
|
||||
type Result = { value: string };
|
||||
>Result : Symbol(Result, Decl(typePredicateStructuralMatch.ts, 6, 24))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 8, 15))
|
||||
|
||||
type Results = Result[];
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
>Result : Symbol(Result, Decl(typePredicateStructuralMatch.ts, 6, 24))
|
||||
|
||||
function isResponseInData<T>(value: T | { data: T}): value is { data: T } {
|
||||
>isResponseInData : Symbol(isResponseInData, Decl(typePredicateStructuralMatch.ts, 9, 24))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 11, 26))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 11, 29))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 11, 26))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 41))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 11, 26))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 11, 29))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 63))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 11, 26))
|
||||
|
||||
return value.hasOwnProperty('data');
|
||||
>value.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 11, 29))
|
||||
>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
|
||||
}
|
||||
|
||||
function getResults1(value: Results | { data: Results }): Results {
|
||||
>getResults1 : Symbol(getResults1, Decl(typePredicateStructuralMatch.ts, 13, 1))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
|
||||
return isResponseInData(value) ? value.data : value;
|
||||
>isResponseInData : Symbol(isResponseInData, Decl(typePredicateStructuralMatch.ts, 9, 24))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
|
||||
>value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
|
||||
}
|
||||
|
||||
function isPlainResponse<T>(value: T | { data: T}): value is T {
|
||||
>isPlainResponse : Symbol(isPlainResponse, Decl(typePredicateStructuralMatch.ts, 17, 1))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 19, 25))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 19, 28))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 19, 25))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 19, 40))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 19, 25))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 19, 28))
|
||||
>T : Symbol(T, Decl(typePredicateStructuralMatch.ts, 19, 25))
|
||||
|
||||
return !value.hasOwnProperty('data');
|
||||
>value.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 19, 28))
|
||||
>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
|
||||
}
|
||||
|
||||
function getResults2(value: Results | { data: Results }): Results {
|
||||
>getResults2 : Symbol(getResults2, Decl(typePredicateStructuralMatch.ts, 21, 1))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 23, 21))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 23, 39))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
>Results : Symbol(Results, Decl(typePredicateStructuralMatch.ts, 8, 32))
|
||||
|
||||
return isPlainResponse(value) ? value : value.data;
|
||||
>isPlainResponse : Symbol(isPlainResponse, Decl(typePredicateStructuralMatch.ts, 17, 1))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 23, 21))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 23, 21))
|
||||
>value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 23, 39))
|
||||
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 23, 21))
|
||||
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 23, 39))
|
||||
}
|
||||
110
tests/baselines/reference/typePredicateStructuralMatch.types
Normal file
110
tests/baselines/reference/typePredicateStructuralMatch.types
Normal file
@@ -0,0 +1,110 @@
|
||||
=== tests/cases/compiler/typePredicateStructuralMatch.ts ===
|
||||
// Repro from #12235
|
||||
|
||||
getResults1([]);
|
||||
>getResults1([]) : Result[]
|
||||
>getResults1 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>[] : undefined[]
|
||||
|
||||
getResults1({data: []});
|
||||
>getResults1({data: []}) : Result[]
|
||||
>getResults1 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>{data: []} : { data: undefined[]; }
|
||||
>data : undefined[]
|
||||
>[] : undefined[]
|
||||
|
||||
getResults2([]);
|
||||
>getResults2([]) : Result[]
|
||||
>getResults2 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>[] : undefined[]
|
||||
|
||||
getResults2({data: []});
|
||||
>getResults2({data: []}) : Result[]
|
||||
>getResults2 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>{data: []} : { data: undefined[]; }
|
||||
>data : undefined[]
|
||||
>[] : undefined[]
|
||||
|
||||
type Result = { value: string };
|
||||
>Result : Result
|
||||
>value : string
|
||||
|
||||
type Results = Result[];
|
||||
>Results : Result[]
|
||||
>Result : Result
|
||||
|
||||
function isResponseInData<T>(value: T | { data: T}): value is { data: T } {
|
||||
>isResponseInData : <T>(value: T | { data: T; }) => value is { data: T; }
|
||||
>T : T
|
||||
>value : T | { data: T; }
|
||||
>T : T
|
||||
>data : T
|
||||
>T : T
|
||||
>value : any
|
||||
>data : T
|
||||
>T : T
|
||||
|
||||
return value.hasOwnProperty('data');
|
||||
>value.hasOwnProperty('data') : boolean
|
||||
>value.hasOwnProperty : (v: string) => boolean
|
||||
>value : T | { data: T; }
|
||||
>hasOwnProperty : (v: string) => boolean
|
||||
>'data' : "data"
|
||||
}
|
||||
|
||||
function getResults1(value: Results | { data: Results }): Results {
|
||||
>getResults1 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>value : Result[] | { data: Result[]; }
|
||||
>Results : Result[]
|
||||
>data : Result[]
|
||||
>Results : Result[]
|
||||
>Results : Result[]
|
||||
|
||||
return isResponseInData(value) ? value.data : value;
|
||||
>isResponseInData(value) ? value.data : value : Result[]
|
||||
>isResponseInData(value) : boolean
|
||||
>isResponseInData : <T>(value: T | { data: T; }) => value is { data: T; }
|
||||
>value : Result[] | { data: Result[]; }
|
||||
>value.data : Result[]
|
||||
>value : { data: Result[]; }
|
||||
>data : Result[]
|
||||
>value : Result[]
|
||||
}
|
||||
|
||||
function isPlainResponse<T>(value: T | { data: T}): value is T {
|
||||
>isPlainResponse : <T>(value: T | { data: T; }) => value is T
|
||||
>T : T
|
||||
>value : T | { data: T; }
|
||||
>T : T
|
||||
>data : T
|
||||
>T : T
|
||||
>value : any
|
||||
>T : T
|
||||
|
||||
return !value.hasOwnProperty('data');
|
||||
>!value.hasOwnProperty('data') : boolean
|
||||
>value.hasOwnProperty('data') : boolean
|
||||
>value.hasOwnProperty : (v: string) => boolean
|
||||
>value : T | { data: T; }
|
||||
>hasOwnProperty : (v: string) => boolean
|
||||
>'data' : "data"
|
||||
}
|
||||
|
||||
function getResults2(value: Results | { data: Results }): Results {
|
||||
>getResults2 : (value: Result[] | { data: Result[]; }) => Result[]
|
||||
>value : Result[] | { data: Result[]; }
|
||||
>Results : Result[]
|
||||
>data : Result[]
|
||||
>Results : Result[]
|
||||
>Results : Result[]
|
||||
|
||||
return isPlainResponse(value) ? value : value.data;
|
||||
>isPlainResponse(value) ? value : value.data : Result[]
|
||||
>isPlainResponse(value) : boolean
|
||||
>isPlainResponse : <T>(value: T | { data: T; }) => value is T
|
||||
>value : Result[] | { data: Result[]; }
|
||||
>value : Result[]
|
||||
>value.data : Result[]
|
||||
>value : { data: Result[]; }
|
||||
>data : Result[]
|
||||
}
|
||||
26
tests/cases/compiler/typePredicateStructuralMatch.ts
Normal file
26
tests/cases/compiler/typePredicateStructuralMatch.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// Repro from #12235
|
||||
|
||||
getResults1([]);
|
||||
getResults1({data: []});
|
||||
|
||||
getResults2([]);
|
||||
getResults2({data: []});
|
||||
|
||||
type Result = { value: string };
|
||||
type Results = Result[];
|
||||
|
||||
function isResponseInData<T>(value: T | { data: T}): value is { data: T } {
|
||||
return value.hasOwnProperty('data');
|
||||
}
|
||||
|
||||
function getResults1(value: Results | { data: Results }): Results {
|
||||
return isResponseInData(value) ? value.data : value;
|
||||
}
|
||||
|
||||
function isPlainResponse<T>(value: T | { data: T}): value is T {
|
||||
return !value.hasOwnProperty('data');
|
||||
}
|
||||
|
||||
function getResults2(value: Results | { data: Results }): Results {
|
||||
return isPlainResponse(value) ? value : value.data;
|
||||
}
|
||||
Reference in New Issue
Block a user