映射类型:删除私有接口

nic*_*ojs 6 typescript mapped-types

在 TypeScript 中,私有属性被视为类型的形状(或接口)的一部分。

class Person {
  constructor(private name: string, public age: number) { }
}
const p: Person = { age: 42 };
// Error: Property 'name' is missing.
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为 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!
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,通常您想忽略私有接口。例如,当您使用依赖项注入和单元测试时。

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);
  });
});
Run Code Online (Sandbox Code Playgroud)

我知道我们可以通过添加一个描述IHttpClient公共接口的接口来解决这个问题,HttpClient并直接使用它而不是类类型,但这需要大量的工作,并且需要手动保持同步。

有没有办法使用映射类型从类型中删除所有非公共属性?

就像是:

type PublicInterface<T> = {
    [P in PublicNames<T>]: T[P];
}
Run Code Online (Sandbox Code Playgroud)

所以它可以用在你不关心隐私的地方:

class MyService {
  constructor(private http: PublicInterface<HttpClient>) {
  }
  // Implementation
}
Run Code Online (Sandbox Code Playgroud)

Ale*_* L. 10

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
Run Code Online (Sandbox Code Playgroud)

操场


或者使用Pick实用程序甚至更短(结果相当于上面的映射类型):

type PublicInterface<T> = Pick<T, keyof T>;
Run Code Online (Sandbox Code Playgroud)

操场

  • 我使用打字稿和映射类型已经有好几年了,现在才注意到这种行为‍♂️ 非常感谢! (3认同)