})
})
+ test('indexed access type', () => {
+ expect(
+ resolve(`
+ type T = { bar: number }
+ type S = { nested: { foo: T['bar'] }}
+ type Target = S['nested']
+ `).props
+ ).toStrictEqual({
+ foo: ['Number']
+ })
+ })
+
+ // test('namespace', () => {
+ // expect(
+ // resolve(`
+ // type T = { foo: number, bar: string, baz: boolean }
+ // type K = 'foo' | 'bar'
+ // type Target = Omit<T, K>
+ // `).props
+ // ).toStrictEqual({
+ // baz: ['Boolean']
+ // })
+ // })
+
describe('errors', () => {
test('error on computed keys', () => {
expect(() => resolve(`type Target = { [Foo]: string }`)).toThrow(
import {
+ Identifier,
Node,
Statement,
TSCallSignatureDeclaration,
TSMappedType,
TSMethodSignature,
TSPropertySignature,
+ TSQualifiedName,
TSType,
TSTypeAnnotation,
TSTypeElement,
case 'TSFunctionType': {
return { props: {}, calls: [node] }
}
+ case 'TSUnionType':
+ case 'TSIntersectionType':
+ return mergeElements(
+ node.types.map(t => resolveTypeElements(ctx, t)),
+ node.type
+ )
+ case 'TSMappedType':
+ return resolveMappedType(ctx, node)
+ case 'TSIndexedAccessType': {
+ if (
+ node.indexType.type === 'TSLiteralType' &&
+ node.indexType.literal.type === 'StringLiteral'
+ ) {
+ const resolved = resolveTypeElements(ctx, node.objectType)
+ const key = node.indexType.literal.value
+ const targetType = resolved.props[key].typeAnnotation
+ if (targetType) {
+ return resolveTypeElements(ctx, targetType.typeAnnotation)
+ } else {
+ break
+ }
+ } else {
+ ctx.error(
+ `Unsupported index type: ${node.indexType.type}`,
+ node.indexType
+ )
+ }
+ }
case 'TSExpressionWithTypeArguments': // referenced by interface extends
case 'TSTypeReference': {
const resolved = resolveTypeReference(ctx, node)
)
}
}
- case 'TSUnionType':
- case 'TSIntersectionType':
- return mergeElements(
- node.types.map(t => resolveTypeElements(ctx, t)),
- node.type
- )
- case 'TSMappedType':
- return resolveMappedType(ctx, node)
}
- ctx.error(`Unsupported type in SFC macro: ${node.type}`, node)
+ ctx.error(`Unresolvable type in SFC macro: ${node.type}`, node)
}
function typeElementsToMap(
if (ref.type === 'Identifier') {
return ref.name
} else {
- // TODO qualified name, e.g. Foo.Bar
- return []
+ return qualifiedNameToPath(ref)
+ }
+}
+
+function qualifiedNameToPath(node: Identifier | TSQualifiedName): string[] {
+ if (node.type === 'Identifier') {
+ return [node.name]
+ } else {
+ return [...qualifiedNameToPath(node.left), node.right.name]
}
}
switch (node.type) {
case 'TSInterfaceDeclaration':
case 'TSEnumDeclaration':
- types[node.id.name] = node
+ case 'TSModuleDeclaration': {
+ const id = node.id.type === 'Identifier' ? node.id.name : node.id.value
+ types[id] = node
break
+ }
case 'TSTypeAliasDeclaration':
types[node.id.name] = node.typeAnnotation
break
case 'TSSymbolKeyword':
return ['Symbol']
+ case 'TSIndexedAccessType': {
+ if (
+ node.indexType.type === 'TSLiteralType' &&
+ node.indexType.literal.type === 'StringLiteral'
+ ) {
+ const resolved = resolveTypeElements(ctx, node.objectType)
+ const key = node.indexType.literal.value
+ return inferRuntimeType(ctx, resolved.props[key])
+ }
+ }
+
default:
return [UNKNOWN_TYPE] // no runtime check
}