如何在TypeScript中确保函数仅接收指定类的实例作为参数
The root problem here is TypeScript's structural type system—it judges type compatibility based on shape, not class names. Since Database and DatabaseEnc share the same public structure (state: string), TypeScript sees them as interchangeable, even though they're separate classes.
To fix this with native TypeScript compile-time checks (no runtime instanceof), we can add a private or protected member to Database that DatabaseEnc doesn't have. TypeScript's structural system respects private/protected members: two types are only compatible if these members come from the same class.
Modified Code Implementation
database.ts
class Database { public state: string = null; // Add a private unique symbol to create a "nominal type" marker private readonly _databaseBrand: unique symbol = Symbol(); constructor(state: string) { this.state = "some state"; } } export default Database;
(A simple private boolean like private readonly _isDatabase: true = true works too—we use unique symbol to avoid accidental naming collisions.)
database_enc.ts (unchanged)
class DatabaseEnc { public state: string = null; constructor() { this.state = "some other state"; } } export default DatabaseEnc;
provider.ts
import Database from "./database"; import DatabaseEnc from "./database_enc"; function checkConn(db: Database): void { console.log(db); } // ❌ Compile-time error now (exactly what we want): // Argument of type 'DatabaseEnc' is not assignable to parameter of type 'Database'. // Property '_databaseBrand' is missing in type 'DatabaseEnc' but required in type 'Database'. const test = new DatabaseEnc(); checkConn(test); // ✅ This still works as intended const test1 = new Database(); checkConn(test1);
Why This Works
The private member acts as a compile-time "brand" that only Database instances possess. TypeScript will reject any type that doesn't have this exact private member from the same class, even if their public properties match.
If you need to extend Database later, use a protected member instead of private—protected members are compatible across class hierarchies but still won't match unrelated classes like DatabaseEnc.
内容的提问来源于stack exchange,提问作者Amir Saleem

