我正在尝试创建一个类型安全版本的get存档函数(参见这里)。我的想法是创建一个函数,如果嵌套的属性可用,则它能够返回正确的类型。
interface Nested {
value: 'some' | 'type';
}
interface SomeComplexType {
foo: string;
bar: number;
nested: Nested;
}
const testObject: SomeComplexType = {
foo: 'foo value',
bar: 1234,
nested: {
value: 'type'
}
};
// The type of someValue should be: 'some' | 'type'
const someValue = lookup(testObject, 'nested.value');
console.log('Value found:', someValue);现在我有以下几点:
function get<T, K extends keyof T>(object: T, key: K): T[K] | undefined {
return object[key];
}
function lookup<T, K extends keyof T>(object: T, path: string) {
const parts = path.split('.');
const property = parts.shift() as K; // TODO autoinfer is possible?
const value = get(object, property);
if (!parts.length || value === undefined) {
return value;
}
const newPath = parts.join('.');
return lookup(value, newPath);
}但是我还是被查找返回类型卡住了。严格模式下的打字稿写着:
src/lookup.ts:14:10 -错误TS7023:'lookup‘隐式具有返回类型'any’,因为它没有返回类型注释,并且在其返回表达式中直接或间接引用。
有什么想法吗?
发布于 2019-03-15 15:52:36
不幸的是,你想要做的事情超出了打字稿的能力,至少在这篇文章中是这样。主要问题是:
as K; // TODO autoinfer is possible?评论TS7023是因为您正在递归到对象中,而且由于我们可以在任何地方递归到对象中,所以TypeScript不确定要在哪里停止,因此它不得不推断返回类型为any。为了解决上面的第二个问题,我们可以尝试将函数声明更改为:
function lookup<T, K extends keyof T>(object: T, path: string): T[K] | undefined {但是,TypeScript将在返回语句中抛出TS2322: Type 'T[K][keyof T[K]] | undefined' is not assignable to type 'T[K] | undefined'。本质上,TypeScript是在说“嵌套的子类型与父类型不一样,所以您编写的内容是错误的”。
尽管如此,在您尝试之前,还有一些已经相当接近了,不过据我所知,还没有一个完美的实现。在这个GitHub问题中,您可以阅读一些不同的解决方案:https://github.com/Microsoft/TypeScript/issues/12290 --其中大多数使用数组而不是字符串,因为TypeScript可以更容易地查看各个片段。
撇开
我想说的是,在TypeScript的世界里,你想要解决的问题是不太合理的。如果您对要访问的对象有了明确的定义--直接访问它就可以了!不需要额外的间接引导。TypeScript将键入-检查访问对象的方式。
如果需要在稍后动态返回特定字段,可以在某个地方创建一个匿名函数,并在稍后调用它,TypeScript可以键入-检查您的匿名函数。
如果您确实需要对没有类型的随机对象进行基于字符串的动态访问,那么any实际上是lookup函数的正确返回类型。因为如果您的代码没有明确的类型保证,那么它很可能是任何东西。
发布于 2019-06-18 19:04:31
我尝试了一些不同的想法,包括:https://github.com/pimterry/typesafe-get和我想我可以分享我自己对这个问题的看法。
我不使用字符串,而是使用封装在try/catch中的箭头函数来解析回退。通过这种方式,我可以查找访问器参数和返回类型,并有机会提供自己的回退:
type CallbackFn<T extends any[] = [], R extends any = void> = (...args: T) => R;
const get = <T extends any, R extends any, F extends any = null>(obj: T, accessor: CallbackFn<[T], R>, fallback: F = null): R | F => {
if (!obj || typeof accessor !== 'function') {
return fallback;
}
try {
return accessor(obj);
} catch (e) {
return fallback;
}
};为此添加了git和更多详细信息:https://github.com/jan-rycko/get-by-accessor
https://stackoverflow.com/questions/55185753
复制相似问题