fix(ivy): types in .d.ts files should account for generics (#24862)

Ivy definition types have a generic type which specifies the return
type of the factory function. For example:

static ngDirectiveDef<NgForOf, '[ngFor][ngForOf]'>

However, in this case NgForOf itself has a type parameter <T>. Thus,
writing the above is incorrect.

This commit modifies ngtsc to understand the genericness of NgForOf and
to write the following:

static ngDirectiveDef<NgForOf<any>, '[ngFor][ngForOf]'>

PR Close #24862
This commit is contained in:
Alex Rickabaugh 2018-07-13 14:49:01 -07:00 committed by Victor Berchet
parent 2b8b647006
commit 41ef75869c
8 changed files with 53 additions and 12 deletions

View File

@ -143,6 +143,7 @@ export function extractDirectiveMetadata(
inputs: {...inputsFromMeta, ...inputsFromFields}, inputs: {...inputsFromMeta, ...inputsFromFields},
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector, outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
type: new WrappedNodeExpr(clazz.name !), type: new WrappedNodeExpr(clazz.name !),
typeArgumentCount: (clazz.typeParameters || []).length,
typeSourceSpan: null !, usesInheritance, typeSourceSpan: null !, usesInheritance,
}; };
return {decoratedElements, decorator: directive, metadata}; return {decoratedElements, decorator: directive, metadata};

View File

@ -305,8 +305,14 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
} }
} }
visitExpressionType(type: ExpressionType, context: Context): any { visitExpressionType(type: ExpressionType, context: Context): string {
return type.value.visitExpression(this, context); const exprStr = type.value.visitExpression(this, context);
if (type.typeParams !== null) {
const typeSegments = type.typeParams.map(param => param.visitType(this, context));
return `${exprStr}<${typeSegments.join(',')}>`;
} else {
return exprStr;
}
} }
visitArrayType(type: ArrayType, context: Context): string { visitArrayType(type: ArrayType, context: Context): string {

View File

@ -47,7 +47,11 @@ export class BuiltinType extends Type {
} }
export class ExpressionType extends Type { export class ExpressionType extends Type {
constructor(public value: Expression, modifiers: TypeModifier[]|null = null) { super(modifiers); } constructor(
public value: Expression, modifiers: TypeModifier[]|null = null,
public typeParams: Type[]|null = null) {
super(modifiers);
}
visitType(visitor: TypeVisitor, context: any): any { visitType(visitor: TypeVisitor, context: any): any {
return visitor.visitExpressionType(this, context); return visitor.visitExpressionType(this, context);
} }
@ -1240,6 +1244,9 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
visitBuiltinType(type: BuiltinType, context: any): any { return this.visitType(type, context); } visitBuiltinType(type: BuiltinType, context: any): any { return this.visitType(type, context); }
visitExpressionType(type: ExpressionType, context: any): any { visitExpressionType(type: ExpressionType, context: any): any {
type.value.visitExpression(this, context); type.value.visitExpression(this, context);
if (type.typeParams !== null) {
type.typeParams.forEach(param => this.visitType(param, context));
}
return this.visitType(type, context); return this.visitType(type, context);
} }
visitArrayType(type: ArrayType, context: any): any { return this.visitType(type, context); } visitArrayType(type: ArrayType, context: any): any { return this.visitType(type, context); }
@ -1496,8 +1503,9 @@ export function importType(
} }
export function expressionType( export function expressionType(
expr: Expression, typeModifiers: TypeModifier[] | null = null): ExpressionType { expr: Expression, typeModifiers: TypeModifier[] | null = null,
return new ExpressionType(expr, typeModifiers); typeParams: Type[] | null = null): ExpressionType {
return new ExpressionType(expr, typeModifiers, typeParams);
} }
export function typeofExpr(expr: Expression) { export function typeofExpr(expr: Expression) {

View File

@ -361,6 +361,11 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
visitExpressionType(ast: o.ExpressionType, ctx: EmitterVisitorContext): any { visitExpressionType(ast: o.ExpressionType, ctx: EmitterVisitorContext): any {
ast.value.visitExpression(this, ctx); ast.value.visitExpression(this, ctx);
if (ast.typeParams !== null) {
ctx.print(null, '<');
this.visitAllObjects(type => this.visitType(type, ctx), ast.typeParams, ctx, ',');
ctx.print(null, '>');
}
return null; return null;
} }

View File

@ -36,3 +36,14 @@ export function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression
throw new Error(`Internal error: Unsupported or unknown metadata: ${meta}`); throw new Error(`Internal error: Unsupported or unknown metadata: ${meta}`);
} }
export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType {
let params: o.Type[]|null = null;
if (numParams > 0) {
params = [];
for (let i = 0; i < numParams; i++) {
params.push(o.DYNAMIC_TYPE);
}
}
return o.expressionType(type, null, params);
}

View File

@ -25,6 +25,11 @@ export interface R3DirectiveMetadata {
*/ */
type: o.Expression; type: o.Expression;
/**
* Number of generic type parameters of the type itself.
*/
typeArgumentCount: number;
/** /**
* A source span for the directive type. * A source span for the directive type.
*/ */

View File

@ -20,11 +20,12 @@ import {ParseSourceSpan, typeSourceSpan} from '../../parse_util';
import {CssSelector, SelectorMatcher} from '../../selector'; import {CssSelector, SelectorMatcher} from '../../selector';
import {BindingParser} from '../../template_parser/binding_parser'; import {BindingParser} from '../../template_parser/binding_parser';
import {OutputContext, error} from '../../util'; import {OutputContext, error} from '../../util';
import * as t from '../r3_ast'; import * as t from '../r3_ast';
import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory'; import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
import {Identifiers as R3} from '../r3_identifiers'; import {Identifiers as R3} from '../r3_identifiers';
import {Render3ParseResult} from '../r3_template_transform'; import {Render3ParseResult} from '../r3_template_transform';
import {typeWithParameters} from '../util';
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api'; import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
import {BindingScope, TemplateDefinitionBuilder} from './template'; import {BindingScope, TemplateDefinitionBuilder} from './template';
import {CONTEXT_NAME, DefinitionMap, ID_SEPARATOR, MEANING_SEPARATOR, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator, unsupported} from './util'; import {CONTEXT_NAME, DefinitionMap, ID_SEPARATOR, MEANING_SEPARATOR, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator, unsupported} from './util';
@ -93,9 +94,10 @@ export function compileDirectiveFromMetadata(
// string literal, which must be on one line. // string literal, which must be on one line.
const selectorForType = (meta.selector || '').replace(/\n/g, ''); const selectorForType = (meta.selector || '').replace(/\n/g, '');
const type = new o.ExpressionType(o.importExpr( const type = new o.ExpressionType(o.importExpr(R3.DirectiveDef, [
R3.DirectiveDef, typeWithParameters(meta.type, meta.typeArgumentCount),
[new o.ExpressionType(meta.type), new o.ExpressionType(o.literal(selectorForType))])); new o.ExpressionType(o.literal(selectorForType))
]));
return {expression, type}; return {expression, type};
} }
@ -167,9 +169,10 @@ export function compileComponentFromMetadata(
const selectorForType = (meta.selector || '').replace(/\n/g, ''); const selectorForType = (meta.selector || '').replace(/\n/g, '');
const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]); const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]);
const type = new o.ExpressionType(o.importExpr( const type = new o.ExpressionType(o.importExpr(R3.ComponentDef, [
R3.ComponentDef, typeWithParameters(meta.type, meta.typeArgumentCount),
[new o.ExpressionType(meta.type), new o.ExpressionType(o.literal(selectorForType))])); new o.ExpressionType(o.literal(selectorForType))
]));
return {expression, type}; return {expression, type};
} }
@ -252,6 +255,7 @@ function directiveMetadataFromGlobalMetadata(
return { return {
name, name,
type: outputCtx.importExpr(directive.type.reference), type: outputCtx.importExpr(directive.type.reference),
typeArgumentCount: 0,
typeSourceSpan: typeSourceSpan:
typeSourceSpan(directive.isComponent ? 'Component' : 'Directive', directive.type), typeSourceSpan(directive.isComponent ? 'Component' : 'Directive', directive.type),
selector: directive.selector, selector: directive.selector,

View File

@ -155,6 +155,7 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
return { return {
name: type.name, name: type.name,
type: new WrappedNodeExpr(type), type: new WrappedNodeExpr(type),
typeArgumentCount: 0,
selector: metadata.selector !, selector: metadata.selector !,
deps: reflectDependencies(type), host, deps: reflectDependencies(type), host,
inputs: {...inputsFromMetadata, ...inputsFromType}, inputs: {...inputsFromMetadata, ...inputsFromType},