在打字稿3.0.3中,我这样导入json文件:
import postalCodes from '../PostalCodes.json';
它具有以下格式:
{
"555": { "code": 555, "city": "Scanning", "isPoBox": true },
"800": { "code": 800, "city": "Høje Taastrup", "isPoBox": true },
"877": { "code": 877, "city": "København C", "isPoBox": true },
"892": { "code": 892, "city": "Sjælland USF P", "isPoBox": true },
"893": { "code": 893, "city": "Sjælland USF B", "isPoBox": true },
"897": { "code": 897, "city": "eBrevsprækken", "isPoBox": true },
"899": { "code": 899, "city": "Kommuneservice", "isPoBox": true },
"900": { "code": 900, "city": "København C", "isPoBox": true },
"910": { "code": 910, "city": "København C", "isPoBox": true },
"917": { "code": 917, "city": "Københavns Pakkecenter", "isPoBox": true },
... and so on
我想这样使用它:
// first.postalCode is of type string
const x = postalCodes[first.postalCode];
我收到错误消息:“元素隐式具有'any'类型,因为类型'...非常长类型签名...'没有索引签名。”
有没有办法使它与自动生成的json类型一起使用,以便我可以通过其字符串键动态地查找邮政编码?
我目前最好的方法是创建一个中间ts文件,例如:
import postalCodes from './PostalCodes.json';
export const PostalCodesLookup = postalCodes as {
[key: string]: { code: number, city: string, isPoBox: boolean }
};
答案 0 :(得分:2)
自TypeScript v2.9起,您可以在 tsconfig.json 文件的resolveJsonModule
中启用compilerOprions
标志,如下所示:
{
"compilerOptions": {
// ... other options
"resolveJsonModule": true
},
}
现在TypeScript应该会自动解析导入的json文件中的类型。
要解决索引类型问题,我可以建议两个选择:
在您的 tsconfig.json 中启用suppressImplicitAnyIndexErrors
。这将消除此错误消息。您也不会得到任何类型提示。
为JSON创建一些类型,并使用这些类型而不仅仅是string
:
import codes from '../codes.json';
type PostalCode = keyof typeof codes;
const goodSring: string = '555';
const badString: string = '2';
const goodCode: PostalCode = '555';
const badCode: PostalCode = '2'; // Error:(39, 7) TS2322: Type '"2"' is not assignable to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"'.
const array: [] = [];
const obj = {some: 'prop'};
const num: number = 123;
const list: PostalCode[] = [
'555',
'2', // Error:(43, 5) TS2322: Type '"2"' is not assignable to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"'.
goodCode,
badCode,
goodSring, // Error:(46, 5) TS2322: Type 'string' is not assignable to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"'.
badString, // Error:(47, 5) TS2322: Type 'string' is not assignable to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"'.
goodSring as PostalCode,
badString as PostalCode, // no protection here
array as PostalCode, // Error:(54, 13) TS2352: Conversion of type '[]' to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type '[]' is not comparable to type '"917"'.
num as PostalCode, // Error:(55, 13) TS2352: Conversion of type 'number' to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
obj as PostalCode, // Error:(56, 13) TS2352: Conversion of type '{ some: string; }' to type '"555" | "800" | "877" | "892" | "893" | "897" | "899" | "900" | "910" | "917"' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type '{ some: string; }' is not comparable to type '"917"'.
];
取决于用法的“硬编码”方式,导出PostalCode
类型可能对您来说很好。
您还可以编写一个在运行时检查JSON的函数:
import codes from '../codes.json';
export type PostalCode = keyof typeof codes;
function verifyCode(s: string): s is PostalCode {
return codes[s as PostalCode] !== undefined; // or use Object.keys, or some other method
}
let city: string;
const str: string = 'asd';
if (verifyCode(str)) {
city = codes[str].city; // in this branch `str` is `PostalCode`
} else {
city = codes[str].city; // complains about the index signature
}
答案 1 :(得分:2)
我认为您的主要问题不是编译器需要以某种不同的方式来推断postalCodes
的类型,而是我们不知道first.postalCode
是{{1的键之一}}。由于postalCodes
的类型为first.postalCode
,因此编译器对此发出警告是合理的。
因此,您需要执行某种type guard才能说服编译器将string
从first.postalCode
缩小到string
。我认为任何内置的控制流类型防护都不会为您进行这种缩小(在某些情况下,keyof typeof postalCodes
会act as a type guard会缩小first.postalCode in postalCodes
的类型,幸运的是,您可以实现user-defined type guard来提供您想要的行为:
postalCodes
然后可以按以下方式使用它:
function isKeyof<T extends object>(obj: T, possibleKey: keyof any): possibleKey is keyof T {
return possibleKey in obj;
}
请注意,如果您仅了解declare const first: {postalCode: string};
if (isKeyof(postalCodes, first.postalCode)) {
const x = postalCodes[first.postalCode]; // no error
} else {
// uh oh, first.postalCode is not valid
}
,那么您确实必须处理first.postalCode
不是postalCodes
的键之一的情况。它是first.postalCode
。
注意事项:string
通常不是完全类型安全的。在TypeScript中,值isKeyOf(obj, key)
的属性可能比编译器在obj
中所知道的更多。也就是说,类型不是exact。在最极端的示例中,如果将keyof typeof obj
声明为类型obj
,则{}
就是keyof typeof obj
,尽管事实上never
可能具有属性。这就是为什么让obj
返回Object.keys(obj)
>的common request总是rejected的原因。
幸运的是,对于带有Array<keyof typeof obj
之类的推断类型的对象文字而言,此警告不是 问题。那是因为您可以肯定postalCodes
是正确的;无需担心其他属性。通常,将typeof postalCodes
缩小为key
并不安全,如
希望有所帮助;祝你好运!
答案 2 :(得分:1)
最近,您还可以对 JSON 文件使用 dynamic imports。
像这样:
const postalCodes: { [key: string]: { code: number, city: string, isPoBox: boolean } } = await import('../PostalCodes.json');
const x = postalCodes[first.postalCode]; // should work just fine here now
或使用显式接口或类型声明(我更喜欢):
interface PostalCodes {
[key: string]: { code: number, city: string, isPoBox: boolean }
}
const postalCodes: PostalCodes = await import('../PostalCodes.json');
const x = postalCodes[first.postalCode]; // should work just fine here now
除了将 resolveJsonModule
编译器选项设置为 true
之外,您还应该为 module
编译器选项使用支持的值。在撰写此答案时,TypeScript 中的动态导入似乎支持模块类型 es2020
、esnext
、commonjs
、amd
、system
或umd
。