Làm thế nào để bạn chỉ định rằng một thuộc tính lớp là một số nguyên?


98

Tôi đang thử nghiệm với TypeScript và trong quá trình tạo một lớp với trường "ID" phải là một số nguyên, tôi đã hơi bối rối.

Trước hết, trong Visual Studio 2012 với plugin TypeScript, tôi thấy "int" trong danh sách các loại của intelliSense. Nhưng tôi gặp lỗi biên dịch cho biết "tên 'int' không tồn tại trong phạm vi hiện tại".

Tôi đã xem xét các thông số kỹ thuật của ngôn ngữ và chỉ thấy các kiểu nguyên thủy sau: số, chuỗi, boolean, null và không xác định. Không có kiểu số nguyên.

Vì vậy, tôi còn lại hai câu hỏi:

  1. Làm cách nào để chỉ ra cho người dùng trong lớp của tôi biết rằng một trường cụ thể không chỉ là "số" mà là một số nguyên (và không bao giờ là dấu phẩy động hoặc số thập phân)?

  2. Tại sao tôi thấy "int" trong danh sách intellisense nếu nó không phải là kiểu hợp lệ?

Cập nhật: Tất cả các câu trả lời mà tôi nhận được cho đến nay là về cách JavaScript không có kiểu int, sẽ rất khó để thực thi kiểu int trong thời gian chạy ... Tôi biết tất cả những điều đó. Tôi đang hỏi liệu có cách TypeScript nào để cung cấp chú thích cho người dùng trong lớp của tôi rằng trường này phải là một số nguyên hay không. Có lẽ một nhận xét của một số định dạng cụ thể?


6
Không, và đó cũng không phải là ID hợp lệ trong ứng dụng của tôi. 10 sẽ là một ID hợp lệ.
— Josh


Một cách để xác thực một trường và đảm bảo rằng chỉ các giá trị số nguyên được chấp nhận, là tạo một phương thức xác thực trước tiên truyền bất kỳ giá trị đầu vào sẽ là nào vào Chuỗi, sử dụng RegEx để kiểm tra giá trị đó a.) Không có dấu thập phân, hoặc b.) nếu tồn tại dấu thập phân, tất cả các ký tự tiếp theo là số 0. Sau đó, phương thức chuyển trở lại kiểu số và kết quả được chuyển đến trường yêu cầu ràng buộc.
— Nathan Toulbert

Câu trả lời:


100
  1. Tôi nghĩ không có cách nào trực tiếp để chỉ định một số là số nguyên hay dấu phẩy động. Trong phần đặc tả TypeScript 3.2.1, chúng ta có thể thấy:

    "... Kiểu nguyên thủy Số tương ứng với kiểu nguyên thủy JavaScript có tên tương tự và đại diện cho các giá trị dấu chấm động IEEE 754 định dạng 64 bit chính xác kép ..."

  2. Tôi nghĩ intlà một lỗi trong Visual Studio intelliSense. Điều chính xác là number.


2
Tôi nghĩ đây là câu trả lời. 1) Bạn không thể chỉ định bất kỳ điều gì chi tiết hơn chỉ đơn giản là "số" cho các loại số, cũng như không có bất kỳ chú thích nào được công nhận để làm như vậy; và 2) có một lỗi trong plugin VS đang đề xuất các loại không hợp lệ, có lẽ dựa trên danh sách các từ khóa dành riêng của ECMA 262 (bao gồm "int")
— Josh

Có thể triển khai kiểu int tĩnh, nhưng họ không chọn. Có thể họ nghĩ rằng nó quá phức tạp để thực hiện?
— trusktr

28

TypeScript là một tập hợp siêu của JavaScript, không có khái niệm về int. Nó chỉ có khái niệm về một con số, có một dấu phẩy động.

Về mặt triết học, khối lượng công việc mà trình biên dịch sẽ phải thực hiện để thực thi chỉ các số nguyên cho kiểu int TypeScript có thể rất lớn và trong một số trường hợp, vẫn không thể đảm bảo tại thời điểm biên dịch rằng chỉ các số nguyên sẽ được gán, điều này là lý do tại sao không thể thêm một int vào TypeScript.

Khi ban đầu bạn nhận được intelliSense trong Visual Studio, công cụ không thể xác định những gì cần cung cấp, vì vậy bạn sẽ nhận được mọi thứ, kể cả int - nhưng một khi bạn đang xử lý một thứ gì đó thuộc loại đã biết, bạn sẽ nhận được intelliSense.

Ví dụ

var myInt: number;
var myString: string;

myInt. // toExponential, toFixed, toPrecision, toString
myString. // charAt, charCodeAt, concat, indexOf, lastIndexOf, length and many more...

2
Nhưng điều đó không trả lời câu hỏi của tôi về cách chú thích lớp của tôi để người dùng biết loại trường thích hợp. Ngoài ra, nếu "int" không bao giờ là một kiểu hợp lệ, tại sao nó lại được đưa vào danh sách "mọi thứ"?
— Josh

Bạn sử dụng public myVariable : number;để khai báo nó dưới dạng một số. intlà một từ dành riêng trong JavaScript, đó là lý do tại sao nó xuất hiện trong danh sách mọi thứ. Thông tin về JavaScript int: javascript.about.com/od/reference/g/rint.htm
— Fenton

public discountPercent : numberkhông cho người dùng biết liệu nó phải là số thập phân (ví dụ: 0,10 cho 10%) hay số nguyên (ví dụ: 10 cho 10%). Không có cách nào để chú thích cho người sử dụng thư viện cái gì hợp lệ và cái gì không?
— Josh

@JoshuaBeall - không có sự khác biệt giữa số thập phân và số nguyên trong JavaScript hoặc TypeScript. Loại số được sử dụng cho cả hai.
— Fenton

1
Bạn @Fenton đã nói: "TypeScript là một tập hợp siêu của JavaScript, không có khái niệm về số nguyên. Nó chỉ có khái niệm về một số, có dấu phẩy động.". Đúng, nhưng thực sự có thể triển khai một intkiểu trong TypeScript, chỉ là họ không chọn.
— trusktr

15

Không có integerhoặc floatnhưng numbernhập TypeScript như trong JavaScript. Nhưng nếu bạn muốn nói với lập trình viên rằng bạn mong đợi integerloại, bạn có thể thử sử dụng Loại bí danh như

type integer = number;
type float = number;

// example:
function setInt(id: integer) {}

nhưng đây vẫn là numberloại và bạn có thể nhận được float.

Một phần mô tả từ tài liệu:
"Bí danh không thực sự tạo ra một kiểu mới - nó tạo ra một tên mới để tham chiếu đến kiểu đó. Đặt biệt hiệu nguyên thủy không hữu ích lắm, mặc dù nó có thể được sử dụng như một dạng tài liệu."


8

Trong TypeScript, bạn có thể ước lượng những gì đôi khi được gọi là kiểu mờ bằng cách sử dụng một điểm đánh dấu.

// Helper for generating Opaque types.
type Opaque<T, K> = T & { __opaque__: K };

// 2 opaque types created with the helper
type Int = Opaque<number, 'Int'>;
type ID = Opaque<number, 'ID'>;

// using our types to differentiate our properties even at runtime
// they are still just numbers
class Foo {
    someId: ID;
    someInt: Int;
}

let foo = new Foo();

// compiler won't let you do this due to or markers
foo.someId = 2;
foo.someInt = 1;

// when assigning, you have to cast to the specific type
// NOTE: This is not completely type safe as you can trick the compiler 
// with something like foo.someId = 1.45 as ID and it won't complain.
foo.someId = 2 as ID;
foo.someInt = 1 as Int;

// you can still consume as numbers
let sum: number = foo.someId + foo.someInt;

Làm điều này cho phép bạn rõ ràng hơn trong mã của mình về loại thuộc tính của bạn mong đợi và trình biên dịch sẽ không cho phép bạn chỉ định một giá trị nguyên thủy mà không cần ép kiểu. Điều này không tạo ra bất kỳ đầu ra .js bổ sung nào và bạn vẫn có thể sử dụng và sử dụng các giá trị như bất kỳ loại nào mà chúng dựa trên. Trong ví dụ này, tôi đang sử dụng số, nhưng bạn cũng có thể sử dụng trên chuỗi và các loại khác.

Bạn vẫn có thể lừa trình biên dịch chấp nhận một cái gì đó không phải là Int hoặc Id trong ví dụ này, nhưng nó sẽ nhảy ra nếu bạn đang cố gắng gán 1,45 là Int hoặc một cái gì đó tương tự. Bạn cũng có tùy chọn tạo các hàm trợ giúp mà bạn sử dụng để tạo các giá trị của mình nhằm cung cấp xác thực thời gian chạy.

Có một số cách khác nhau để bạn có thể tạo các loại "được đánh dấu". Đây là một bài viết hay: https://michalzalecki.com/nominal-typing-in-typescript/


Như đã nói ở những nơi khác, khả năng sử dụng thực tế của điều này hơi đáng nghi ngờ, nhưng bạn có thể đi xa hơn bằng cách thêm một bộ đánh chữ và / hoặc một Trình trợ giúp đúc, điều này sẽ dễ dàng đối với một số thứ - như ví dụ Int - và hầu như không thể với những người khác - như ví dụ ID: // @ts-ignore const isInt = (val: number | Int): val is Int => !(val % 1); const Integer = (val: number): Int => Math.round(val) as any;
— Sam96

4

Đây là kết quả hàng đầu trên Google đối với tôi vì vậy tôi nghĩ tôi nên cung cấp các giải pháp mà tôi tìm thấy.

Sử dụng bigint

Bây giờ là năm 2020 và bigintđã được chấp nhận, nó xứng đáng được đề cập đến. Bạn có thể làm đơn giản như dưới đây. Lưu ý rằng bigints đi kèm với tác động hiệu suất lớn hơn so với a number.

const myNumber: bigint = 10n


Sử dụng loại danh nghĩa / loại được gắn thẻ / loại mờ

Một giải pháp thay thế là sử dụng một loại danh nghĩa, nhưng nó được cho là kém tiện dụng hơn và tôi không chắc liệu nó có nhanh hơn hay không bigint, nhưng mô hình chung chung cho bất kỳ loại nào, không chỉ number. TypeScript không có hỗ trợ "hạng nhất" cho việc này, vì vậy bạn phải thực hiện một vụ hack táo tợn. Có một thư viện cho điều này được gọi là newtype-tsbao gồm các loại phổ biến như Integervậy, vì vậy bạn có thể chỉ muốn sử dụng nó, nhưng tôi sẽ giải thích hoạt động bên dưới.

Để bắt đầu, chúng tôi xác định integerloại.

const TAG = Symbol()
type integer = number & { readonly [TAG]: unique symbol }

Các TAG đảm bảo rằng chúng ta có một giá trị duy nhất để chúng ta không vô tình tạo ra một đối tượng có cùng một khóa và chúng ta cũng đặt trường trở thành một biểu tượng duy nhất vì cùng một lý do. Bây giờ, số nguyên của bạn sẽ không thực sự có trường đối tượng này nhưng điều đó tốt.

Với điều này, bạn vẫn có thể thêm integervào numberviệc sử dụng +. Không tốt. Vì vậy, bạn có thể thực thi an toàn kiểu đối với các đối số ở đây bằng cách kết hợp hệ thống kiểu với một hàm. Tôi chỉ sẽ gọi nó là bảo vệ, và một lần nữa như bạn có thể thấy nó không dành riêng cho integers - bạn có thể tạo ra các loại mờ hơn và sử dụng lại.

type guard = <A>(f: (...ns: Array<A>) => A, ...ns: Array<A>) => A
const guard: guard = (f, ...ns) => f(...ns)

Nếu bạn cố gắng gọi điều đó bằng number

const bad: integer = guard((a, b) => a + b as integer, myCoolInteger, 10)

bạn sẽ gặp lỗi như bên dưới

Argument of type '10' is not assignable to parameter of type 'integer'.
  Type '10' is not assignable to type '{ readonly [TAG]: unique symbol; }'.(2345)

Lưu ý rằng bạn không thực thi kiểu trả về ở đây (vì bạn phải sử dụng as integer) và một số toán tử như /sẽ trả về số dấu phẩy động, vì vậy bạn có thể vẫn muốn thực hiện kiểm tra thời gian chạy hoặc thêm một Math.roundvào phiên bản chuyên biệt của guard, nhưng điều này ít nhất sẽ đảm bảo bạn không cố gắng sử dụng hai loại số riêng biệt với nhau - tưởng tượng bạn có GBPvà USDvà cố gắng thêm những người, mà có thể sẽ không được những gì bạn dự định.


2

Như bạn đã thấy, typecript không có kiểu dữ liệu float như ngôn ngữ javascript. Chỉ có cái numberđó bao gồm tất cả intvà doublecùng một lúc; có thể bạn phải tạo một hàm nhận một số và kiểm tra xem nó có phải là inthay không double, bằng cách trả về một số trạng thái trong trường hợp lỗi / thành công. Một cái gì đó như thế này như là phương thức của lớp của bạn:

function SetN(x:number) {
   var is_int = parseInt(x) === parseFloat(x);
   if(is_int) this.n = x;
   return is_int;
}

//..
y = 10.5;
if(SetN(y)) {
  //OK
} else {
   //error not set y isn't a int
}

Lưu ý: nó không hoạt động đối với 10.0ví dụ: Nếu bạn không thực sự muốn nó, có thể bạn phải chuyển nó thành chuỗi và cố gắng tìm một ..


Điều này sẽ giúp ích cho việc thực thi thời gian chạy nhưng tôi chủ yếu quan tâm đến việc thông báo cho người dùng những con số nào là hợp lệ.
— Josh

1

intđã được dành riêng cho từ khóa sử dụng trong tương lai trong các phiên bản trước của javascript (ECMAScript nếu bạn thích). Nhưng nó là một từ hợp lệ bây giờ (trong đó "bây giờ" tương đương với "trong thông số kỹ thuật mới nhất").

Ví dụ, vào năm 262, nó vẫn còn được bảo lưu, http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

Nó sẽ bổ sung tốt hơn cho bảng chữ để có một intkiểu dữ liệu được triển khai nhưng với tất cả các quy tắc kiểm tra và truyền kiểu thời gian biên dịch có sẵn.


Vì vậy, có lẽ intellisense đang chọn "từ dành riêng" này mặc dù nó không được trình biên dịch nhận dạng?
— Josh

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.