打字稿如何在父函数上使用“未知”类型来确定子函数的类型?
Typescript how to use `unknown` type on a parent function to then determine the type on a child function?
我希望能够确定 myValue
对应于哪个接口。鉴于以下情况,我如何根据 testZero()
的 return 值记录 a
或 b
?
export interface ITest {
id: string
isLatest: boolean
createdAt: number
updatedAt: number
}
export interface ITestTwo {
id: string
isLatest: boolean
createdAt: string // This value was changed for this example
updatedAt: number
}
function testZero(): unknown {
return {
id: '123',
isLatest: true,
createdAt: '123',
updatedAt: 456
}
}
function testOne(): ITest | ITestTwo {
const myValue = testZero()
// THE FOLLOWING `IF` STATEMENT DOES NOT WORK AND IS WHAT I AM TRYING TO SOLVE
if (typeof t === typeof ITest) {
console.log('a')
} else if (typeof myValue === typeof ITestTwo) {
console.log('b')
}
}
testOne()
您将需要编写运行时代码来检查值是否符合接口。当 TypeScript 代码编译为 JavaScript 时,TypeScript 接口本身是 erased。 TypeScript 类型系统旨在在运行时描述 类型;它不会影响 运行时。所以 ITest
和 ITestTwo
将不会被检查。
此外,runtime typeof
operator 将检查其操作数的类型和 return 一小组字符串值中的一个;在你的情况下,你可以期望 typeof myValue
无论如何都是 "object"
。所以你不能使用 typeof
来检查 myValue
本身。充其量您需要检查 myValue
的属性并对其使用 typeof
。
一种方法是编写一些 type guard functions 以编译器识别为类型检查的方式表示此运行时检查。对于您可以使用运行时 typeof
运算符检查每个 属性 的“简单”对象类型,您可以编写一个通用检查函数,然后根据您的需要对其进行专门化。广义函数:
type TypeofMapping = {
string: string;
boolean: boolean;
number: number;
object: object;
undefined: undefined;
}
const simpleObjGuard = <T extends { [K in keyof T]: keyof TypeofMapping }>
(obj: T) => (x: any): x is { [K in keyof T]: TypeofMapping[T[K]] } => typeof x === "object" && x &&
(Object.keys(obj) as Array<keyof T>).every(k => k in x && typeof x[k] === obj[k]);
然后你可以专门为 ITest
和 ITestTwo
:
const isITest: (x: any) => x is ITest =
simpleObjGuard({ id: "string", isLatest: "boolean", createdAt: "number", updatedAt: "number" });
const isITestTwo: (x: any) => x is ITestTwo =
simpleObjGuard({ id: "string", isLatest: "boolean", createdAt: "string", updatedAt: "number" });
因此 isITest()
函数将检查其参数是否与 ITest
兼容,而 isITestTwo()
函数将检查其参数是否与 ITestTwo
兼容。在任何一种情况下,编译器都会将 true
结果解释为参数可以从 unknown
缩小到您正在检查的类型的证据:
function testOne(): ITest | ITestTwo {
const myValue = testZero()
if (isITest(myValue)) {
console.log("a "+myValue.createdAt.toFixed())
return myValue;
} else if (isITestTwo(myValue)) {
console.log("b "+myValue.createdAt.toUpperCase())
return myValue;
}
throw new Error("Guess it wasn't one of those");
}
console.log()
和 return
行不会导致编译器错误这一事实表明编译器将 myValue
视为窄化类型。在运行时,您可以检查它是否也有效:
testOne() // b 123
有一些库可以为您编写这些类型保护函数...我认为 io-ts
可以做到这一点,并且可以以一种很好的方式编写 serialization/deserialization 代码使用 TypeScript 编译器。或者您可以自己编写,如上面的 simpleObjGuard
。
好的,希望对您有所帮助;祝你好运!
我希望能够确定 myValue
对应于哪个接口。鉴于以下情况,我如何根据 testZero()
的 return 值记录 a
或 b
?
export interface ITest {
id: string
isLatest: boolean
createdAt: number
updatedAt: number
}
export interface ITestTwo {
id: string
isLatest: boolean
createdAt: string // This value was changed for this example
updatedAt: number
}
function testZero(): unknown {
return {
id: '123',
isLatest: true,
createdAt: '123',
updatedAt: 456
}
}
function testOne(): ITest | ITestTwo {
const myValue = testZero()
// THE FOLLOWING `IF` STATEMENT DOES NOT WORK AND IS WHAT I AM TRYING TO SOLVE
if (typeof t === typeof ITest) {
console.log('a')
} else if (typeof myValue === typeof ITestTwo) {
console.log('b')
}
}
testOne()
您将需要编写运行时代码来检查值是否符合接口。当 TypeScript 代码编译为 JavaScript 时,TypeScript 接口本身是 erased。 TypeScript 类型系统旨在在运行时描述 类型;它不会影响 运行时。所以 ITest
和 ITestTwo
将不会被检查。
此外,runtime typeof
operator 将检查其操作数的类型和 return 一小组字符串值中的一个;在你的情况下,你可以期望 typeof myValue
无论如何都是 "object"
。所以你不能使用 typeof
来检查 myValue
本身。充其量您需要检查 myValue
的属性并对其使用 typeof
。
一种方法是编写一些 type guard functions 以编译器识别为类型检查的方式表示此运行时检查。对于您可以使用运行时 typeof
运算符检查每个 属性 的“简单”对象类型,您可以编写一个通用检查函数,然后根据您的需要对其进行专门化。广义函数:
type TypeofMapping = {
string: string;
boolean: boolean;
number: number;
object: object;
undefined: undefined;
}
const simpleObjGuard = <T extends { [K in keyof T]: keyof TypeofMapping }>
(obj: T) => (x: any): x is { [K in keyof T]: TypeofMapping[T[K]] } => typeof x === "object" && x &&
(Object.keys(obj) as Array<keyof T>).every(k => k in x && typeof x[k] === obj[k]);
然后你可以专门为 ITest
和 ITestTwo
:
const isITest: (x: any) => x is ITest =
simpleObjGuard({ id: "string", isLatest: "boolean", createdAt: "number", updatedAt: "number" });
const isITestTwo: (x: any) => x is ITestTwo =
simpleObjGuard({ id: "string", isLatest: "boolean", createdAt: "string", updatedAt: "number" });
因此 isITest()
函数将检查其参数是否与 ITest
兼容,而 isITestTwo()
函数将检查其参数是否与 ITestTwo
兼容。在任何一种情况下,编译器都会将 true
结果解释为参数可以从 unknown
缩小到您正在检查的类型的证据:
function testOne(): ITest | ITestTwo {
const myValue = testZero()
if (isITest(myValue)) {
console.log("a "+myValue.createdAt.toFixed())
return myValue;
} else if (isITestTwo(myValue)) {
console.log("b "+myValue.createdAt.toUpperCase())
return myValue;
}
throw new Error("Guess it wasn't one of those");
}
console.log()
和 return
行不会导致编译器错误这一事实表明编译器将 myValue
视为窄化类型。在运行时,您可以检查它是否也有效:
testOne() // b 123
有一些库可以为您编写这些类型保护函数...我认为 io-ts
可以做到这一点,并且可以以一种很好的方式编写 serialization/deserialization 代码使用 TypeScript 编译器。或者您可以自己编写,如上面的 simpleObjGuard
。
好的,希望对您有所帮助;祝你好运!