如何理解复杂的打字稿泛型类型?

时间:2019-07-16 06:36:37

标签: angular reactjs typescript rxjs6 typescript-generics

我正在检查RxJS api文档。在理解上面写的怪异语法时,我总是会遇到问题,例如以下示例:

<ng-container matColumnDef="{{column}}" *ngFor="let column of columnsToDisplay">
  <ng-container *ngIf="column === 'select'; else notSelect">
    <th mat-header-cell *matHeaderCellDef>
      <mat-checkbox (change)="$event ? masterToggle() : null"
                    [checked]="selection.hasValue() && isAllSelected()"
                    [indeterminate]="selection.hasValue() && !isAllSelected()">
      </mat-checkbox>
    </th>
    <td mat-cell *matCellDef="let row">
      <mat-checkbox (click)="$event.stopPropagation()"
                    (change)="$event ? selection.toggle(row) : null"
                    [checked]="selection.isSelected(row)">
      </mat-checkbox>
    </td>
  </ng-container>
  <ng-template #notSelect>
    <th mat-header-cell *matHeaderCellDef> {{column}}</th>
    <td mat-cell *matCellDef="let element"> {{element[column]}} </td>
  </ng-template>
</ng-container>

我想了解这种语法,然后自己编写。

因此,如果有人解释上面示例中的情况,将为您提供很大的帮助。

1 个答案:

答案 0 :(得分:6)

function combineLatest<O extends ObservableInput<any>, R>(...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]): Observable<R>

让我们分解一下。

好的,那是一个功能

function combineLatest(args): Result

,它带有一些参数并返回类型为Result的结果

但这也是一种特殊的函数,称为generic函数。 泛型的目的是在成员之间提供有意义的类型约束。

function combineLatest<T>(args): Result
                      /\
           now it's a generic function

这里的T是一个 type变量,它使我们能够捕获用户提供的类型,以便以后可以使用该信息。例如,我们可以使用T作为返回类型:

function combineLatest<T>(args): T

以便进行以下调用:

combineLatest<string>(someParams) // returns `string`

由于我们将string显式设置为T,因此将得到类型为string的结果。

我们还可以将T类型的变量用于该函数的任何参数。

function combineLatest<T>(arg: T): T

现在我们可以使用 type参数推断

combineLatest('someStr') // returns `string`

也就是说,我们告诉编译器根据传递的参数类型为我们自动设置T的值。我们传递了string,以便string

我们可以根据需要使用任意多个类型变量。我们也可以随便叫他们。

function combineLatest<Type1, Type2, ...>(...)

泛型很棒,有时候我们想用传入的参数来做一些动作:

function combineLatest<T>(arg: T): T {
  arg.name = arg.name.toLowerCase(); // Property 'name' does not exist on type 'T'
  return arg;
}

如您所见,编译器无法理解哪种类型的arg。为了解决这个问题,我们可以借助T运算符来表示对extends类型变量的约束:

interface ItemWithName {
  name: string;
}
function combineLatest<T extends ItemWithName>(arg: T): T {
  arg.name = arg.name.toLowerCase();
  return arg;
}

现在,编译器知道arg具有name的{​​{1}}属性。


现在让我们回到我们的初始声明:

string

我们可以在这里看到此函数使用两个类型变量:

  • combineLatest<O extends ObservableInput<any>, R>(...): Observable<R> 类型变量,表示可观察。限制为O
  • 类型
  • ObservableInput<any>代表结果

R函数的结果应为combineLatest类型的Observable。例如,我们可以通过调用以下函数来强制该类型:

R

现在让我们看一下该函数可以采用的参数:

combineLatest<any, MyClass>(...)  // returns Observable<MyClass>

我们可以在此处看到rest parameters运算符。让我们简化上面的表达式,以便我们想象像这样的东西:

...observables: (O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike)[]

这意味着...observables: CombinedType[] 函数可以接受零个或多个参数,这些参数随后将被收集到一个变量combineLatest中。

observables

那么此参数的类型是什么

combineLatest();                 // without parameters
combineLatest(obs1);             // one parameter
combineLatest(obs1, obs2, ..etc) // many parameters

可以是:

  • CombinedType O | ((...values: ObservedValueOf<O>[]) => R) | SchedulerLike 类型变量的类型,该变量被限制为我们前面讨论的O类型

  • ,它可以是函数ObservableInput<any>,该函数接受零个或多个类型为(...values: ObservedValueOf<O>[]) => R的参数,并返回类型为ObservedValueOf<O>的变量类型。请注意,此返回类型可用于推断R函数的返回类型。

  • ,它可以是combineLatest界面的类型。


我认为,如果将TypeScript类型声明分成小块,那么理解TypeScript类型声明应该没有任何问题。