首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Typescript:扩展JSON类型的接口

Typescript:扩展JSON类型的接口
EN

Stack Overflow用户
提问于 2022-08-08 15:54:58
回答 1查看 92关注 0票数 1

我在类型记录中使用一个通用的JSON类型,这是来自这里的建议

代码语言:javascript
复制
type JSONValue = 
 | string
 | number
 | boolean
 | null
 | JSONValue[]
 | {[key: string]: JSONValue}

我希望能够从与JSON类型匹配的接口类型中转换到JSON类型。例如:

代码语言:javascript
复制
interface Foo {
  name: 'FOO',
  fooProp: string
}

interface Bar {
  name: 'BAR',
  barProp: number;
}

const genericCall = (data: {[key: string]: JSONValue}): Foo | Bar | null => {
  if ('name' in data && data['name'] === 'FOO')
    return data as Foo;
  else if ('name' in data && data['name'] === 'BAR')
    return data as Bar;
  return null;
}

此操作当前失败,因为类型记录看不到接口如何与JSONValue相同类型:

代码语言:javascript
复制
Conversion of type '{ [key: string]: JSONValue; }' to type 'Foo' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Property 'name' is missing in type '{ [key: string]: JSONValue; }' but required in type 'Foo'.

但是从分析上来说,我们当然知道这是可以的,因为我们认识到在运行时类型Foo和Bar是JSON兼容的。我如何告诉打字稿,这是一个好的演员?

ETA:我可以按照错误信息,先转换到未知,但我不想这么做--如果TS真的理解了差异,那就更好了,我想知道这是否可能。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-10 03:01:48

这里的问题是编译器没有使用check if ('name' in data && data['name'] === 'FOO')从其原始类型的中提取data的类型。类型{[key: string]: JSONValue}不是友联市,目前操作员检查仅缩小联合类型的值。微软/打字稿#21732有一个开放的特性请求来进行这样的缩小,但目前它并不是语言的一部分。

这意味着检查后data保持为{[key: string]: JSONValue}类型。然后,当您试图通过断言确认dataFoo类型时,编译器警告您您可能犯了错误,因为它没有看到Foo{[key: string]: JSONValue}是关系足够密切的类型。

如果您确信您所做的是一个很好的检查,则始终可以使用编译器建议并对与Foo{[key: string]: JSONValue} (例如unknown )相关的中间类型进行断言。

代码语言:javascript
复制
return data as unknown as Foo; // okay

如果这与您有关,那么您可以编写您自己的用户定义类型保护功能,它执行您期望从if ('name' in data && data['name'] === 'FOO')中缩小的范围。本质上,如果该检查通过,那么我们就知道data{name: 'FOO'}类型的,它与类型断言的Foo有足够的关联。下面是一个可能的类型保护功能:

代码语言:javascript
复制
function hasKeyVal<K extends PropertyKey, V extends string | number |
  boolean | null | undefined | bigint>(
    obj: any, k: K, v: V): obj is { [P in K]: V } {
  return obj && obj[k] === v;
}

因此,您不必编写if ('name' in data && data['name'] === 'FOO'),而是编写if (hasKeyVal(data, 'name', 'FOO'))。返回类型obj is {[P in K]: V}意味着如果函数返回true,编译器应该将obj类型缩小到具有键类型为K且值为V类型的属性的对象。让我们来测试一下:

代码语言:javascript
复制
const genericCall = (data: { [key: string]: JSONValue }): Foo | Bar | null => {
  if (hasKeyVal(data, 'name', 'FOO'))
    return data as Foo; // okay, data is now {name: 'FOO'} which is related to Foo
  else if (hasKeyVal(data, 'name', 'BAR'))
    return data as Bar;  // okay, data is now {name: 'BAR'} which is related to Bar
  return null;
}

现在起作用了。hasKeyVal()检查将data缩小到具有正确类型的name属性的对象,这与FooBar有足够的关联,类型断言才能成功(类型断言仍然是必要的,因为如果Foo具有其他属性,则{name: 'Foo'}类型的值可能不是Foo )。

操场链接到代码

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73280790

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档