Hạn chế chặt chẽ hơn trong TypeScript chỉ được phép nếu trường hợp bị thu hẹp là có thể xảy ra
Trong đoạn mã sau
interface C1 { kind: 'c1' }
interface C2 { kind: 'c2' }
interface C3<T> { kind: 'c3'; value: T }
function isC3<T, CX extends ([C3<T>] extends [CX] ? { kind: string; } : never)>(
c: CX
): c is C3<T> /* <------- PROBLEM !!! */ {
return c.kind === 'c3';
}
// valid use
type C = C1 | C2 | C3<number>;
isC3<number, C>({ kind: 'c3', value: 1 }); // true
isC3<number, C>({ kind: 'c1' }); // false
// invalid use
type D = C1 | C2;
declare var d: D;
isC3<number, D>(d); // D doesn't satisfy constraint never (as expected)
Tôi đang cố gắng tạo hàm bảo vệ kiểu isC3 sao cho việc gọi nó chỉ biên dịch khi kiểu của đối số kiểu được cho có trường hợp C3 trong đó, nếu không tôi muốn nhận được lỗi biên dịch (vui lòng xem các ví dụ)
Vì vậy mọi thứ hoạt động, ngoại trừ việc tôi nhận được một lỗi tại kiểu trả về của typeguard:
A type predicate's type must be assignable to its parameter's type.
Type 'C3<T>' is not assignable to type 'CX'.
'C3<T>' is assignable to the constraint of type 'CX', but 'CX' could be instantiated with a different subtype of constraint '{ kind: string; }'.ts(2677)
Có cách nào để có được những gì tôi đang tìm không?
Câu trả lời #1
Nếu bạn có một kiểu T mà bạn biết có thể gán cho U nhưng trình biên dịch không biết điều này, và bạn cần sử dụng T ở một nơi mà trình biên dịch mong đợi một cái gì đó có thể gán cho U, bạn có thể sử dụng kiểu tiện ích Extract<T, U> hoặc có thể là Extract<U, T>, hoặc bạn có thể sử dụng phép giao T & U, tùy thuộc vào trường hợp sử dụng của bạn:
declare function isC3<T, CX extends ([C3<T>] extends [CX] ? { kind: string; } : never)>(
c: CX
): c is Extract<CX, C3<T>> // okay
hoặc
function isC3<T, CX extends ([C3<T>] extends [CX] ? { kind: string; } : never)>(
c: CX
): c is CX & C3<T> {
return c.kind === 'c3';
}
Cả hai cái này cuối cùng đều tạo ra một kiểu về cơ bản là C3<T>. Extract<T, U> lọc một hợp trong T chỉ còn những thành viên có thể gán cho U (vì vậy nếu bạn kỳ vọng CX là một hợp, thì Extract<CX, C3<T>> là hợp lý), trong khi T & U thì ít thiên vị về các hợp hơn.
Liên kết Playground đến mã
