Interface naming conventions when mocking classes in unit tests with TypeScript

When mocking classes in TypeScript, I’ve found that the class type includes both private and # members. My mocks for the public members of the class don’t satisfy the class.

class AClass {

  #a: string = "A";

  public A() {
    return this.#a;
  }

  private B() {
    return "B";
  }

}

// ERROR: Type '{ A: () => string; }' is missing the following properties from type 'AClass': #a, B ts(2739)
const mockAClass: AClass = {
  A: () => "Mock A"
};

I can get around it by creating an “interface type” like this, and changing the function expecting AClass to expect IAClass instead:

type IAClass = {
  A: () => string;
};

const mockIAClass: IAClass = {
  A: () => "Mock A"
};

The “I” prefix is common in C# but I haven’t really seen this in anywhere in TypeScript. Is there a best practice for naming interface like types in TypeScript?

The classes I’m mocking have names like ServiceClient, UsersTable, etc. Before I start making interface types like IServiceClient and IUsersTable, or ServiceClientInterface & UsersTableInterface I wanted to check if there is a recommended naming convention in TypeScript.

Or perhaps there is a better TypeScript solution for this like hiding the private members from the class somehow?

Why Your Mock Fails

In TypeScript:

class AClass {
  #a: string = "A";
  public A() {
    return this.#a;
  }
  private B() {
    return "B";
  }
}

When you try this:

const mockAClass: AClass = {
  A: () => "Mock A"
}; //  Error: Missing #a and private B

You’re seeing the error because:

  • AClass includes private and #private members in its type definition.
  • TypeScript enforces that an object literal fully satisfies all class members, even the private ones.
  • You cannot mock private members directly in an object literal.

Correct Solution

Use a public interface or public method-only type for mocking. Example:

type AClassPublic = Pick<AClass, 'A'>;

const mock: AClassPublic = {
  A: () => "Mock A"
};

You don’t need to manually create IAClass — just infer the public shape using utility types like Pick.


Best Practice for Naming

TypeScript community prefers:

  • Descriptive names, without I prefix
  • Avoid C#-style IService, IUserRepo, etc.

Preferred:

type UserServiceLike = Pick<UserService, 'getUser' | 'saveUser'>;

Or if you define a separate interface manually:

interface UserService {
  getUser(): User;
  saveUser(user: User): void;
}

Avoid (C# style):

interface IUserService { ... }

Avoid (verbose):

type UserServiceInterface = { ... };

Bonus: Another Approach (Abstract Class)

If you want to enforce a contract but still use class types:

abstract class AbstractAClass {
  abstract A(): string;
}

const mock: AbstractAClass = {
  A: () => "Mock A"
};

This works because abstract class types don’t include private or # members.


Summary

Problem Solution
Mocking fails due to private members Use Pick<Class, 'publicMethod'> or define an interface
Naming interfaces No I prefix — use meaningful names like UserService, UserRepoContract, or SomethingLike
Alternative Use abstract class to define contracts without private members