Variance (variance)
Trong TypeScript, khi xác định tính tương thích của các kiểu, khái niệm variance được sử dụng. Variance biểu thị mối quan hệ giữa các kiểu, và trong TypeScript, để biểu thị variance này, ta thêm in hoặc out trước type variable của generics.
Lưu ý, variance được đề cập ở đây cũng có thể thay đổi bằng cấu hình strictFunctionTypes trong tsconfig.json.
Trong phần này, trừ khi có đề cập đặc biệt, ta giả định strictFunctionTypes là false.
📄️ strictFunctionTypes
Làm nghiêm ngặt check variance của parameter type
Covariance (Covariance)
Covariance là variance khi thêm out vào type variable của generics. Covariance có nghĩa là mối quan hệ subtype được bảo toàn.
Contravariance (Contravariance)
Contravariance là variance khi thêm in vào type variable của generics. Contravariance có nghĩa là mối quan hệ subtype bị đảo ngược.
Invariance (Invariance)
Invariance là variance khi thêm cả in và out vào type variable của generics. Invariance có nghĩa là kiểu không covariant cũng không contravariant.
Bivariance (Bivariance)
Bivariance là variance khi không thêm in hoặc out vào type variable của generics.
Ở đây, ta định nghĩa BivariantFunction<I, O> (không thêm variance nên trong TypeScript giống như bivariant) là kiểu của hàm nhận một tham số I và trả về giá trị O.
tstypeBivariantFunction <I ,O > = (arg :I ) =>O ;
tstypeBivariantFunction <I ,O > = (arg :I ) =>O ;
Tiếp theo, định nghĩa CovariantFunction<in I, O> với tham số I là covariant, ContravariantFunction<I, out O> với giá trị trả về O là contravariant, và InvariantFunction<in out I, in out O> với cả tham số và giá trị trả về đều invariant. Chúng được định nghĩa như sau:
tstypeBivariantFunction <I ,O > = (arg :I ) =>O ;typeCovariantFunction <I , outO > =BivariantFunction <I ,O >;typeContravariantFunction <inI ,O > =BivariantFunction <I ,O >;typeInvariantFunction <in outI , in outO > =BivariantFunction <I ,O >;
tstypeBivariantFunction <I ,O > = (arg :I ) =>O ;typeCovariantFunction <I , outO > =BivariantFunction <I ,O >;typeContravariantFunction <inI ,O > =BivariantFunction <I ,O >;typeInvariantFunction <in outI , in outO > =BivariantFunction <I ,O >;
Ví dụ sử dụng quan hệ kế thừa class
Để dễ hiểu quan hệ kế thừa, định nghĩa các class A, B, C. A kế thừa B, B kế thừa C, và đã thêm các method.
tsclassA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}
tsclassA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}
Định nghĩa các hàm với cả I và O đều là B cho mỗi loại variance.
tsdeclare constbiFunc :BivariantFunction <B ,B >;declare constcoFunc :CovariantFunction <B ,B >;declare constcontraFunc :ContravariantFunction <B ,B >;declare constinFunc :InvariantFunction <B ,B >;
tsdeclare constbiFunc :BivariantFunction <B ,B >;declare constcoFunc :CovariantFunction <B ,B >;declare constcontraFunc :ContravariantFunction <B ,B >;declare constinFunc :InvariantFunction <B ,B >;
Thử thay đổi generics của các hàm này.
tsconstf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constf03 :BivariantFunction <B ,A > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f04 BivariantFunction <B ,C > =biFunc ;constf05 :CovariantFunction <B ,A > =coFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f06 CovariantFunction <B ,C > =coFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f07 ContravariantFunction <A ,B > =contraFunc ;constf08 :ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Property 'c' is missing in type 'B' but required in type 'C'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Property 'b' is missing in type 'A' but required in type 'B'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f12 InvariantFunction <B ,C > =inFunc ;
tsconstf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constf03 :BivariantFunction <B ,A > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f04 BivariantFunction <B ,C > =biFunc ;constf05 :CovariantFunction <B ,A > =coFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f06 CovariantFunction <B ,C > =coFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f07 ContravariantFunction <A ,B > =contraFunc ;constf08 :ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<A, B>'. Property 'b' is missing in type 'A' but required in type 'B'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Property 'c' is missing in type 'B' but required in type 'C'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Property 'b' is missing in type 'A' but required in type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, A>'. Property 'b' is missing in type 'A' but required in type 'B'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Property 'c' is missing in type 'B' but required in type 'C'.: f12 InvariantFunction <B ,C > =inFunc ;
Tóm tắt những trường hợp xảy ra lỗi:
f04: Tham số và giá trị trả về đều bivariant nên mối quan hệ supertype và subtype được chấp nhận, nhưngBkhông có methodc()so với giá trị trả vềCnên xảy ra lỗif06: Giá trị trả về là covariant nên việc gán supertype được chấp nhận, nhưng giá trị trả vềClà subtype củaBnên xảy ra lỗif07: Tham số là contravariant nên việc gán subtype được chấp nhận, nhưng tham sốAlà supertype củaBnên xảy ra lỗif09: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif10: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif11: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif12: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗi
Ngoài ra, khi đặt strictFunctionTypes là true, ngoài các lỗi trên còn có:
f01: Tham số và giá trị trả về đều bivariant nên mối quan hệ supertype và subtype được chấp nhận, nhưng tham sốAkhông có methodb()nên xảy ra lỗi
Ví dụ sử dụng union type
Hãy biểu diễn quan hệ kế thừa bằng union type.
tstypeA = null;typeB = null | undefined;typeC = null | undefined | string;
tstypeA = null;typeB = null | undefined;typeC = null | undefined | string;
Lúc này A là subtype của B, và B là subtype của C. Nói cách khác, A extends B và B extends C.
tsconstf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f03 BivariantFunction <B ,A > =biFunc ;constf04 :BivariantFunction <B ,C > =biFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f05 CovariantFunction <B ,A > =coFunc ;constf06 :CovariantFunction <B ,C > =coFunc ;constf07 :ContravariantFunction <A ,B > =contraFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f08 ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f12 InvariantFunction <B ,C > =inFunc ;
tsconstf01 :BivariantFunction <A ,B > =biFunc ;constf02 :BivariantFunction <C ,B > =biFunc ;constType 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'BivariantFunction<B, B>' is not assignable to type 'BivariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f03 BivariantFunction <B ,A > =biFunc ;constf04 :BivariantFunction <B ,C > =biFunc ;constType 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'CovariantFunction<B, B>' is not assignable to type 'CovariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f05 CovariantFunction <B ,A > =coFunc ;constf06 :CovariantFunction <B ,C > =coFunc ;constf07 :ContravariantFunction <A ,B > =contraFunc ;constType 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'ContravariantFunction<B, B>' is not assignable to type 'ContravariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f08 ContravariantFunction <C ,B > =contraFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<null, B>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f09 InvariantFunction <A ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<C, B>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f10 InvariantFunction <C ,B > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, null>'. Type 'B' is not assignable to type 'null'. Type 'undefined' is not assignable to type 'null'.: f11 InvariantFunction <B ,A > =inFunc ;constType 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.2322Type 'InvariantFunction<B, B>' is not assignable to type 'InvariantFunction<B, C>'. Type 'C' is not assignable to type 'B'. Type 'string' is not assignable to type 'B'.: f12 InvariantFunction <B ,C > =inFunc ;
f03: Tham số và giá trị trả về đều bivariant nên mối quan hệ supertype và subtype được chấp nhận, nhưngundefinedcủaBkhông thể gán chonullcủa giá trị trả vềAnên xảy ra lỗif05: Giá trị trả về là covariant nên việc gán supertype được chấp nhận, nhưngundefinedcủaBkhông thể gán cho giá trị trả vềAlànullnên xảy ra lỗif08: Tham số là contravariant nên việc gán subtype được chấp nhận, nhưngstringcủa tham sốCkhông thể gán choBnên xảy ra lỗif09: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif10: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif11: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗif12: Tham số và giá trị trả về đều invariant nên mối quan hệ supertype và subtype không được chấp nhận, do đó xảy ra lỗi
Khi đặt strictFunctionTypes là true, ngoài các lỗi trên còn có:
f02: Tham số và giá trị trả về đều bivariant nên mối quan hệ supertype và subtype được chấp nhận, nhưngstringcủa tham sốCkhông thể gán choBnên xảy ra lỗi
Kiểm tra quan hệ kế thừa
Hãy sử dụng Conditional Types để kiểm tra quan hệ kế thừa. Định nghĩa kiểu IsSubType<T, U> để xác định xem kiểu T có phải là subtype của U hay không.
tstypeIsSubType <T ,U > =T extendsU ? true : false;
tstypeIsSubType <T ,U > =T extendsU ? true : false;
Áp dụng IsSubType<T, U> cho các class A, B, C trước đó.
tsclassA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
tsclassA {publica (): void {console .log ("a");}}classB extendsA {publicb (): void {console .log ("b");}}classC extendsB {publicc (): void {console .log ("c");}}declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
Có thể thấy chỉ trả về true khi là chính class đó hoặc subclass.
Hãy xem union type. Lưu ý, vì đây là union type nên sử dụng Distributive Conditional Type.
📄️ Distributive Conditional Types
Distributive Conditional Types (có thể được gọi là kiểu điều kiện phân phối, phân phối union type, v.v.) chỉ tính chất khi Conditional Types được áp dụng cho generic type và type argument là union type, thì điều kiện sẽ được áp dụng riêng lẻ (= phân phối) cho từng thành viên cấu thành union type đó.
tstypeIsSubType <T ,U > = [T ] extends [U ] ? true : false;
tstypeIsSubType <T ,U > = [T ] extends [U ] ? true : false;
tstypeA = null;typeB = null | undefined;typeC = null | undefined | string;declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
tstypeA = null;typeB = null | undefined;typeC = null | undefined | string;declare constt1 :IsSubType <A ,A >;declare constt2 :IsSubType <B ,B >;declare constt3 :IsSubType <C ,C >;declare constt4 :IsSubType <A ,B >;declare constt5 :IsSubType <B ,C >;declare constt6 :IsSubType <A ,C >;declare constt7 :IsSubType <B ,A >;declare constt8 :IsSubType <C ,B >;declare constt9 :IsSubType <C ,A >;
Có thể thấy t4, t5, t6 trả về true vì A là subtype của B, và B là subtype của C.