pulumi/pulumi · error · Error

Component '${componentName}' constructor 'args' parameter mu

Error message

Component '${componentName}' constructor 'args' parameter must have a type

What it means

Beyond being named `args`, the parameter must carry an explicit type annotation so the analyzer can inspect the args interface via the TypeScript checker. An untyped (implicitly any) parameter gives the checker nothing to enumerate into schema properties.

Source

Thrown at sdk/nodejs/provider/experimental/analyzer.ts:317

    private analyzeComponent(node: typescript.ClassDeclaration): ComponentDefinition {
        const componentName = node.name?.text!;

        // We expect exactly 1 constructor, and it must have and 'args'
        // parameter that has an interface type.
        const constructors = node.members.filter((member: typescript.ClassElement) =>
            ts.isConstructorDeclaration(member),
        ) as typescript.ConstructorDeclaration[];
        if (constructors.length !== 1) {
            throw new Error(`Component '${componentName}' must have exactly one constructor`);
        }
        const argsParam = constructors?.[0].parameters.find((param: typescript.ParameterDeclaration) => {
            return ts.isIdentifier(param.name) && param.name.escapedText === "args";
        });
        if (!argsParam) {
            throw new Error(`Component '${componentName}' constructor must have an 'args' parameter`);
        }
        if (!argsParam.type) {
            throw new Error(`Component '${componentName}' constructor 'args' parameter must have a type`);
        }
        const args = this.checker.getTypeAtLocation(argsParam.type);
        const argsSymbol = args.getSymbol();
        if (!argsSymbol || !isInterface(argsSymbol)) {
            throw new Error(`Component '${componentName}' constructor 'args' parameter must be an interface`);
        }

        let inputs: Record<string, PropertyDefinition> = {};
        // Use args.getProperties() instead of argsSymbol.members to include
        // properties inherited from extended interfaces.
        const argsProperties = args.getProperties();
        if (argsProperties.length > 0) {
            inputs = this.analyzeSymbols(
                { component: componentName, inputOutput: InputOutput.Neither, typeName: argsSymbol.getName() },
                argsProperties,
            );
        }

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Add an explicit interface type to the args parameter
  2. Define the inputs as an exported interface (e.g. `interface MyComponentArgs {...}`) and annotate the parameter with it
  3. Enable noImplicitAny to catch this at compile time

Example fix

// before
constructor(args) { ... }
// after
constructor(args: MyComponentArgs) { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

function argsParamTyped(c: any): boolean { const m = c.toString().match(/constructor\(\s*args\s*\)\s*{/); return !m; }

Type guard

function hasTypedArgs<T extends new (args: I) => any, I>(c: T): c is T { return true; }

Prevention

When it happens

Trigger: Declaring `constructor(args)` or `constructor(args: any)` with no concrete interface type annotation.

Common situations: Quick prototypes without typed args; implicit-any codebases without strict checking.

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/26d50af4f43a7495. Report an issue: GitHub.