映射类型: 移除私有接口

6
在TypeScript中,私有属性被视为类型的形状(或接口)的一部分。
class Person {
  constructor(private name: string, public age: number) { }
}
const p: Person = { age: 42 };
// Error: Property 'name' is missing.

这是有效的,因为TypeScript需要跟踪私有成员。

class Person {
  constructor(private name: string, public age: number) { }
  equals(other: Person) {
    return this.name === other.name && this.age === other.age;
    // This is valid, because TypeScript kept track of the private `name` property!
  }
}

然而,通常情况下,您希望忽略私有接口。例如,在使用依赖注入和单元测试时。

class HttpClient {
   constructor(private log: Logger) {
   }
   async doGet(url: string) { 
      return (await fetch(url)).json();
   }
}

class MyService {
  constructor(private http: HttpClient) {
  }
  // Implementation
}

// Unit test for MyService:
describe('MyService', () => {
  it('should work', () => {
    const httpMock: HttpClient = { // ERROR: Property 'log' is missing
      doGet(url: string) {
         return Promise.resolve({ name: 'Han' });
      }
    };
    const sut = new MyService(httpMock);
  });
});

我知道我们可以通过添加IHttpClient接口来解决这个问题,该接口描述了HttpClient的公共接口,并使用它代替直接使用类类型,但这需要很多工作,并且需要手动保持同步。

是否有一种使用映射类型从类型中删除所有非公共属性的方法?

类似于:

type PublicInterface<T> = {
    [P in PublicNames<T>]: T[P];
}

因此,它可以在您不关心私密性的场所使用:
class MyService {
  constructor(private http: PublicInterface<HttpClient>) {
  }
  // Implementation
}
1个回答

9

keyof足够智能,只可以查看公共键:

class Person {
  constructor(private name: string, public age: number) { }
}

type PublicInterface<T> = {
    [P in keyof T]: T[P];
}

const p: PublicInterface<Person> = { age: 42 }; // no error

Playground


使用 Pick 工具类型可以更简短地实现相同的映射类型:

type PublicInterface<T> = Pick<T, keyof T>;

Playground


3
我已经使用 TypeScript 和映射类型工作了几年,但直到现在才注意到这种行为。谢谢! - nicojs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接