})
})
})
+
+ describe('template literals', () => {
+ test('mapped types with string type', () => {
+ expect(
+ resolve(`
+ type X = 'a' | 'b'
+ defineProps<{[K in X as \`\${K}_foo\`]: string}>()
+ `).props,
+ ).toStrictEqual({
+ a_foo: ['String'],
+ b_foo: ['String'],
+ })
+ })
+
+ // #10962
+ test('mapped types with generic parameters', () => {
+ const { props } = resolve(`
+ type Breakpoints = 'sm' | 'md' | 'lg'
+ type BreakpointFactory<T extends string, V> = {
+ [K in Breakpoints as \`\${T}\${Capitalize<K>}\`]: V
+ }
+ type ColsBreakpoints = BreakpointFactory<'cols', number>
+ defineProps<ColsBreakpoints>()
+ `)
+ expect(props).toStrictEqual({
+ colsSm: ['Number'],
+ colsMd: ['Number'],
+ colsLg: ['Number'],
+ })
+ })
+ })
})
function resolve(
node.type,
)
case 'TSMappedType':
- return resolveMappedType(ctx, node, scope)
+ return resolveMappedType(ctx, node, scope, typeParameters)
case 'TSIndexedAccessType': {
const types = resolveIndexType(ctx, node, scope)
return mergeElements(
ctx: TypeResolveContext,
node: TSMappedType,
scope: TypeScope,
+ typeParameters?: Record<string, Node>,
): ResolvedElements {
const res: ResolvedElements = { props: {} }
- const keys = resolveStringType(ctx, node.typeParameter.constraint!, scope)
+ let keys: string[]
+ if (node.nameType) {
+ const { name, constraint } = node.typeParameter
+ scope = createChildScope(scope)
+ Object.assign(scope.types, { ...typeParameters, [name]: constraint })
+ keys = resolveStringType(ctx, node.nameType, scope)
+ } else {
+ keys = resolveStringType(ctx, node.typeParameter.constraint!, scope)
+ }
for (const key of keys) {
res.props[key] = createProperty(
{