Hãy nhớ rằng, TS không bao giờ chèn polyfill độn vào mã của bạn. Đó không phải là mục tiêu của nó.
Target cho TS biết thông số kỹ thuật ES nào bạn muốn mã cuối cùng/mã biên dịch hỗ trợ. Nếu bạn định cấu hình nó là ES5, TS sẽ biên dịch xuống các tính năng cú pháp thành ES5, do đó bất kỳ hàm mũi tên nào () => {} trong mã của bạn sẽ được chuyển đổi thành hàm () {}.
Bất cứ điều gì bạn chọn cho mục tiêu đều ảnh hưởng đến giá trị mặc định của lib, giá trị này sẽ cho TS biết định nghĩa kiểu nào cần đưa vào dự án của bạn. Nếu bạn có "target": "es5", giá trị mặc định của lib sẽ là ["dom", "es5", "ScriptHost"]. Giá trị này giả định các tính năng chức năng nào mà trình duyệt sẽ hỗ trợ khi chạy. Việc thêm các thứ vào lib chỉ để làm TS hài lòng - bạn vẫn cần tự nhập polyfill vào dự án.
Bất cứ điều gì bạn chọn cho mục tiêu đều ảnh hưởng đến giá trị mặc định của lib, giá trị này sẽ cho TS biết định nghĩa kiểu nào cần đưa vào dự án của bạn. Nếu bạn có "target": "es5", giá trị mặc định của lib sẽ là ["dom", "es5", "ScriptHost"]. Giá trị này giả định các tính năng chức năng nào mà trình duyệt sẽ hỗ trợ khi chạy. Việc thêm các thứ vào lib chỉ để làm TS hài lòng - bạn vẫn cần tự nhập polyfill vào dự án.
Ví dụ: Bạn cần hỗ trợ IE11 nhưng bạn cũng muốn sử dụng promises. IE11 hỗ trợ ES5, nhưng promises là một tính năng của ES6. Bạn nhập polyfill promises, nhưng TS vẫn báo lỗi. Bây giờ bạn chỉ cần cho TypeScript biết rằng mã của bạn sẽ nhắm mục tiêu đến ES5 và có thể sử dụng promises trong cơ sở mã một cách an toàn:
Typescript không có bất kỳ kiểu tích hợp nào, tất cả các kiểu đều xuất phát từ một tập hợp các định nghĩa cơ sở (nằm trong thư mục lib trong thư mục cài đặt Typescript). Theo mặc định, mục tiêu xác định các lib nào được bao gồm. Ví dụ, tài liệu nêu:
Lưu ý: Nếu --lib không được chỉ định, danh sách librare mặc định sẽ được đưa vào. Các thư viện mặc định được đưa vào là:
Ý tưởng cơ bản là trong khi mục tiêu xử lý các tính năng ngôn ngữ (cụ thể hơn là các tính năng ngôn ngữ nào cần được biên dịch xuống, ví dụ: for-of hoặc các hàm mũi tên), thì tùy chọn lib xử lý các tiện ích mà môi trường thời gian chạy có (tức là các đối tượng tích hợp trông như thế nào, chúng là gì).
Lý tưởng nhất là nên sử dụng các thư viện mặc định cho một mục tiêu nhất định. Tuy nhiên, chúng ta có thể có một môi trường hỗ trợ một số tiện ích thời gian chạy nhưng không hỗ trợ các tính năng ngôn ngữ hoặc chúng ta có thể nhắm mục tiêu thời gian chạy với phiên bản es thấp hơn và poly-fill một số tiện ích thời gian chạy, điều này thường có thể thực hiện được đối với một số thứ (ví dụ: Promises).
classTodoItem {privatereadonly _creationTimestamp:number;privatereadonly _identifier:string;constructor(private _description:string, identifier?:string) {this._creationTimestamp =newDate().getTime();if (identifier) {this._identifier = identifier; } else {// this is just for the example; for any real project, use// UUIDs instead: https://www.npmjs.com/package/uuidthis._identifier =Math.random().toString(36).substr(2,9); } }getcreationTimestamp():number {returnthis._creationTimestamp; }getidentifier():string {returnthis._identifier; }getdescription():string {returnthis._description; }}classTodoList {private _todoList:ReadonlyArray<TodoItem> = [];constructor(todoList?:TodoItem[]) {// first we make sure that we have received a valid array// reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArrayif (Array.isArray(todoList) &&todoList.length) {this._todoList =this._todoList.concat(todoList); } }gettodoList():ReadonlyArray<TodoItem> {returnthis._todoList }addTodo(todoItem:TodoItem) {if (todoItem) {// the value is "truthy":// not null, not undefined, not NaN, not an empty string, not 0, not falsethis._todoList =this._todoList.concat(todoItem); } }removeTodo(itemId:string) {if (itemId) {this._todoList =this._todoList.filter(item => {if (item.identifier === itemId) {returnfalse; // drop } else {returntrue; // keep } }); } }}interfaceTodoListView {render(todoList:ReadonlyArray<TodoItem>):void;getInput():TodoItem;getFilter():string;clearInput():void;filter():void;}classHTMLTodoListViewimplementsTodoListView {privatereadonly todoInput:HTMLInputElement;privatereadonly todoListDiv:HTMLDivElement;privatereadonly todoListFilter:HTMLInputElement;constructor() {this.todoInput =document.getElementById('todoInput') asHTMLInputElement;this.todoListDiv =document.getElementById('todoListContainer') asHTMLDivElement;this.todoListFilter =document.getElementById('todoFilter') asHTMLInputElement;// defensive checksif (!this.todoInput) {thrownewError("Could not find the todoInput HTML input element. Is the HTML correct?"); }if (!this.todoListDiv) {thrownewError("Could not find the todoListContainer HTML div. Is the HTML correct?"); }if (!this.todoListFilter) {thrownewError("Could not find the todoFilter HTML input element. Is the HTML correct?"); } }clearInput():void {this.todoInput.value =''; }getFilter():string {returnthis.todoListFilter.value.toUpperCase(); }getInput():TodoItem {consttodoInputValue:string=this.todoInput.value.trim();constretVal:TodoItem=newTodoItem(todoInputValue);return retVal; }render(todoList:ReadonlyArray<TodoItem>):void {console.log("Updating the rendered todo list");// we sort the given listconstsortedTodoList=todoList.slice(0,todoList.length).sort((a, b) => {if (a.description <b.description) {return-1; } elseif (a.description ===b.description) {return0; } else {return1; } });this.todoListDiv.innerHTML ='';this.todoListDiv.textContent =''; // Edge, ...constul=document.createElement('ul');ul.setAttribute('id','todoList');this.todoListDiv.appendChild(ul);sortedTodoList.forEach(item => {constli=document.createElement('li');li.setAttribute('class','todo-list-item');li.innerHTML =`<a href='#' onclick='todoIt.removeTodo("${item.identifier}")'>${item.description}</a>`;ul.appendChild(li); }); }filter():void {console.log("Filtering the rendered todo list");consttodoListHtml:HTMLUListElement=document.getElementById('todoList') asHTMLUListElementif (todoListHtml ==null) {console.log("Nothing to filter");return; }consttodoListFilterText=this.getFilter();todoListHtml.childNodes.forEach((item) => {let itemText:string|null=item.textContent;if (itemText !==null) { itemText =itemText.toUpperCase();if (itemText.startsWith(todoListFilterText)) { (item asHTMLLIElement).style.display ="list-item"; } else { (item asHTMLLIElement).style.display ="none"; } } }); }}interfaceTodoListController {addTodo():void;filterTodoList():void;removeTodo(identifier:string):void;}classTodoItimplementsTodoListController {privatereadonly _todoList:TodoList=newTodoList();constructor(private _todoListView:TodoListView) {console.log("TodoIt");if (!_todoListView) {thrownewError("The todo list view implementation is required to properly initialize TodoIt!"); } }addTodo():void {// get the value from the viewconstnewTodo=this._todoListView.getInput();// verify that there is something to addif (''!==newTodo.description) {console.log("Adding todo: ", newTodo);// add the new item to the list (i.e., update the model)this._todoList.addTodo(newTodo);console.log("New todo list: ",this._todoList.todoList);// clear the inputthis._todoListView.clearInput();// update the rendered todo listthis._todoListView.render(this._todoList.todoList);// filter the list if neededthis.filterTodoList(); } }filterTodoList():void {this._todoListView.filter(); }removeTodo(identifier:string):void {if (identifier) {console.log("item to remove: ", identifier);this._todoList.removeTodo(identifier);this._todoListView.render(this._todoList.todoList);this.filterTodoList(); } }}classEventUtils {staticisEnter(event:KeyboardEvent):boolean {let isEnterResult =false;if (event !==undefined&&event.defaultPrevented) {returnfalse; }if (event ==undefined) { isEnterResult =false; } elseif (event.key !==undefined) { isEnterResult =event.key ==='Enter'; } elseif (event.keyCode !==undefined) { isEnterResult =event.keyCode ===13; }return isEnterResult; }}constview=newHTMLTodoListView();consttodoIt=newTodoIt(view);
index.html
<!DOCTYPEhtml><htmllang="en"><head> <metacharset="UTF-8"> <title>TodoIt</title></head><bodyid="todoIt"style="text-align: center"> <h1>TodoIt</h1> <h2>Add new items</h2> <p> What needs to be done? <inputtype="text"id="todoInput"title="What should be added to the todo list?"onkeyup="if(EventUtils.isEnter(event)) { todoIt.addTodo() }"onblur="todoIt.addTodo()" /> <inputtype="button"value="Add todo"onclick="todoIt.addTodo()" /> </p> <h2>Current todo list</h2> <p>Filter: <inputtype="text"id="todoFilter"title="Filter for the todo list"onkeyup="todoIt.filterTodoList()"onblur="todoIt.filterTodoList()" /></p> <divid="todoListContainer">Nothing to do, hurray!</div> <scriptsrc="./app.js"></script></body></html>
app.ts
classTodoItem {privatereadonly _creationTimestamp:number;privatereadonly _identifier:string;constructor(private _description:string, identifier?:string) {this._creationTimestamp =newDate().getTime();if (identifier) {this._identifier = identifier; } else {// this is just for the example; for any real project, use// UUIDs instead: https://www.npmjs.com/package/uuidthis._identifier =Math.random().toString(36).substr(2,9); } }getcreationTimestamp():number {returnthis._creationTimestamp; }getidentifier():string {returnthis._identifier; }getdescription():string {returnthis._description; }}classTodoList {private _todoList:ReadonlyArray<TodoItem> = [];constructor(todoList?:TodoItem[]) {// first we make sure that we have received a valid array// reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArrayif (Array.isArray(todoList) &&todoList.length) {this._todoList =this._todoList.concat(todoList); } }gettodoList():ReadonlyArray<TodoItem> {returnthis._todoList }addTodo(todoItem:TodoItem) {if (todoItem) {// the value is "truthy":// not null, not undefined, not NaN, not an empty string, not 0, not falsethis._todoList =this._todoList.concat(todoItem); } }removeTodo(itemId:string) {if (itemId) {this._todoList =this._todoList.filter(item => {if (item.identifier === itemId) {returnfalse; // drop } else {returntrue; // keep } }); } }}interfaceTodoListView {render(todoList:ReadonlyArray<TodoItem>):void;getInput():TodoItem;getFilter():string;clearInput():void;filter():void;}classHTMLTodoListViewimplementsTodoListView {privatereadonly todoInput:HTMLInputElement;privatereadonly todoListDiv:HTMLDivElement;privatereadonly todoListFilter:HTMLInputElement;constructor() {this.todoInput =document.getElementById('todoInput') asHTMLInputElement;this.todoListDiv =document.getElementById('todoListContainer') asHTMLDivElement;this.todoListFilter =document.getElementById('todoFilter') asHTMLInputElement;// defensive checksif (!this.todoInput) {thrownewError("Could not find the todoInput HTML input element. Is the HTML correct?"); }if (!this.todoListDiv) {thrownewError("Could not find the todoListContainer HTML div. Is the HTML correct?"); }if (!this.todoListFilter) {thrownewError("Could not find the todoFilter HTML input element. Is the HTML correct?"); } }clearInput():void {this.todoInput.value =''; }getFilter():string {returnthis.todoListFilter.value.toUpperCase(); }getInput():TodoItem {consttodoInputValue:string=this.todoInput.value.trim();constretVal:TodoItem=newTodoItem(todoInputValue);return retVal; }render(todoList:ReadonlyArray<TodoItem>):void {console.log("Updating the rendered todo list");// we sort the given listconstsortedTodoList=todoList.slice(0,todoList.length).sort((a, b) => {if (a.description <b.description) {return-1; } elseif (a.description ===b.description) {return0; } else {return1; } });this.todoListDiv.innerHTML ='';this.todoListDiv.textContent =''; // Edge, ...constul=document.createElement('ul');ul.setAttribute('id','todoList');this.todoListDiv.appendChild(ul);sortedTodoList.forEach(item => {constli=document.createElement('li');li.setAttribute('class','todo-list-item');li.innerHTML =`<a href='#' onclick='todoIt.removeTodo("${item.identifier}")'>${item.description}</a>`;ul.appendChild(li); }); }filter():void {console.log("Filtering the rendered todo list");consttodoListHtml:HTMLUListElement=document.getElementById('todoList') asHTMLUListElementif (todoListHtml ==null) {console.log("Nothing to filter");return; }consttodoListFilterText=this.getFilter();todoListHtml.childNodes.forEach((item) => {let itemText:string|null=item.textContent;if (itemText !==null) { itemText =itemText.toUpperCase();if (itemText.startsWith(todoListFilterText)) { (item asHTMLLIElement).style.display ="list-item"; } else { (item asHTMLLIElement).style.display ="none"; } } }); }}interfaceTodoListController {addTodo():void;filterTodoList():void;removeTodo(identifier:string):void;}classTodoItimplementsTodoListController {privatereadonly _todoList:TodoList=newTodoList();constructor(private _todoListView:TodoListView) {console.log("TodoIt");if (!_todoListView) {thrownewError("The todo list view implementation is required to properly initialize TodoIt!"); } }addTodo():void {// get the value from the viewconstnewTodo=this._todoListView.getInput();// verify that there is something to addif (''!==newTodo.description) {console.log("Adding todo: ", newTodo);// add the new item to the list (i.e., update the model)this._todoList.addTodo(newTodo);console.log("New todo list: ",this._todoList.todoList);// clear the inputthis._todoListView.clearInput();// update the rendered todo listthis._todoListView.render(this._todoList.todoList);// filter the list if neededthis.filterTodoList(); } }filterTodoList():void {this._todoListView.filter(); }removeTodo(identifier:string):void {if (identifier) {console.log("item to remove: ", identifier);this._todoList.removeTodo(identifier);this._todoListView.render(this._todoList.todoList);this.filterTodoList(); } }}classEventUtils {staticisEnter(event:KeyboardEvent):boolean {let isEnterResult =false;if (event !==undefined&&event.defaultPrevented) {returnfalse; }if (event ==undefined) { isEnterResult =false; } elseif (event.key !==undefined) { isEnterResult =event.key ==='Enter'; } elseif (event.keyCode !==undefined) { isEnterResult =event.keyCode ===13; }return isEnterResult; }}constview=newHTMLTodoListView();consttodoIt=newTodoIt(view);
➜ typescript yarn build
yarn run v1.22.22
$ tsc
Done in 5.19s.
➜ typescript
app.js
"use strict";var TodoItem =/** @class */ (function () {functionTodoItem(_description, identifier) {this._description = _description;this._creationTimestamp =newDate().getTime();if (identifier) {this._identifier = identifier; }else {// this is just for the example; for any real project, use// UUIDs instead: https://www.npmjs.com/package/uuidthis._identifier =Math.random().toString(36).substr(2,9); } }Object.defineProperty(TodoItem.prototype,"creationTimestamp", {get:function () {returnthis._creationTimestamp; }, enumerable:false, configurable:true });Object.defineProperty(TodoItem.prototype,"identifier", {get:function () {returnthis._identifier; }, enumerable:false, configurable:true });Object.defineProperty(TodoItem.prototype,"description", {get:function () {returnthis._description; }, enumerable:false, configurable:true });return TodoItem;}());var TodoList =/** @class */ (function () {functionTodoList(todoList) {this._todoList = [];// first we make sure that we have received a valid array// reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArrayif (Array.isArray(todoList) &&todoList.length) {this._todoList =this._todoList.concat(todoList); } }Object.defineProperty(TodoList.prototype,"todoList", {get:function () {returnthis._todoList; }, enumerable:false, configurable:true });TodoList.prototype.addTodo=function (todoItem) {if (todoItem) {// the value is "truthy":// not null, not undefined, not NaN, not an empty string, not 0, not falsethis._todoList =this._todoList.concat(todoItem); } };TodoList.prototype.removeTodo=function (itemId) {if (itemId) {this._todoList =this._todoList.filter(function (item) {if (item.identifier === itemId) {returnfalse; // drop }else {returntrue; // keep } }); } };return TodoList;}());var HTMLTodoListView =/** @class */ (function () {functionHTMLTodoListView() {this.todoInput =document.getElementById('todoInput');this.todoListDiv =document.getElementById('todoListContainer');this.todoListFilter =document.getElementById('todoFilter');// defensive checksif (!this.todoInput) {thrownewError("Could not find the todoInput HTML input element. Is the HTML correct?"); }if (!this.todoListDiv) {thrownewError("Could not find the todoListContainer HTML div. Is the HTML correct?"); }if (!this.todoListFilter) {thrownewError("Could not find the todoFilter HTML input element. Is the HTML correct?"); } }HTMLTodoListView.prototype.clearInput=function () {this.todoInput.value =''; };HTMLTodoListView.prototype.getFilter=function () {returnthis.todoListFilter.value.toUpperCase(); };HTMLTodoListView.prototype.getInput=function () {var todoInputValue =this.todoInput.value.trim();var retVal =newTodoItem(todoInputValue);return retVal; };HTMLTodoListView.prototype.render=function (todoList) {console.log("Updating the rendered todo list");// we sort the given listvar sortedTodoList =todoList.slice(0,todoList.length).sort(function (a, b) {if (a.description <b.description) {return-1; }elseif (a.description ===b.description) {return0; }else {return1; } });this.todoListDiv.innerHTML ='';this.todoListDiv.textContent =''; // Edge, ...var ul =document.createElement('ul');ul.setAttribute('id','todoList');this.todoListDiv.appendChild(ul);sortedTodoList.forEach(function (item) {var li =document.createElement('li');li.setAttribute('class','todo-list-item');li.innerHTML ="<a href='#' onclick='todoIt.removeTodo(\"".concat(item.identifier,"\")'>").concat(item.description,"</a>");ul.appendChild(li); }); };HTMLTodoListView.prototype.filter=function () {console.log("Filtering the rendered todo list");var todoListHtml =document.getElementById('todoList');if (todoListHtml ==null) {console.log("Nothing to filter");return; }var todoListFilterText =this.getFilter();todoListHtml.childNodes.forEach(function (item) {var itemText =item.textContent;if (itemText !==null) { itemText =itemText.toUpperCase();if (itemText.startsWith(todoListFilterText)) {item.style.display ="list-item"; }else {item.style.display ="none"; } } }); };return HTMLTodoListView;}());var TodoIt =/** @class */ (function () {functionTodoIt(_todoListView) {this._todoListView = _todoListView;this._todoList =newTodoList();console.log("TodoIt");if (!_todoListView) {thrownewError("The todo list view implementation is required to properly initialize TodoIt!"); } }TodoIt.prototype.addTodo=function () {// get the value from the viewvar newTodo =this._todoListView.getInput();// verify that there is something to addif (''!==newTodo.description) {console.log("Adding todo: ", newTodo);// add the new item to the list (i.e., update the model)this._todoList.addTodo(newTodo);console.log("New todo list: ",this._todoList.todoList);// clear the inputthis._todoListView.clearInput();// update the rendered todo listthis._todoListView.render(this._todoList.todoList);// filter the list if neededthis.filterTodoList(); } };TodoIt.prototype.filterTodoList=function () {this._todoListView.filter(); };TodoIt.prototype.removeTodo=function (identifier) {if (identifier) {console.log("item to remove: ", identifier);this._todoList.removeTodo(identifier);this._todoListView.render(this._todoList.todoList);this.filterTodoList(); } };return TodoIt;}());var EventUtils =/** @class */ (function () {functionEventUtils() { }EventUtils.isEnter=function (event) {var isEnterResult =false;if (event !==undefined&&event.defaultPrevented) {returnfalse; }if (event ==undefined) { isEnterResult =false; }elseif (event.key !==undefined) { isEnterResult =event.key ==='Enter'; }elseif (event.keyCode !==undefined) { isEnterResult =event.keyCode ===13; }return isEnterResult; };return EventUtils;}());var view =newHTMLTodoListView();var todoIt =newTodoIt(view);
I have still not been able to find good answer for this. The "target" option defines, what version of Javascript the result will run on. The "lib" option is less clearly described anywhere. Seems like it is a more granular way to describe the target environment, but then it seems strange it affects what you can write in the .ts source files. Thought TS what as superset of JS, so why does it affect whether, e.g., Promise() is available or not? This seems like it does not only define the target but also affect what functions you have available in Typescript.
Can someone explain that clearly or direct to an answer (there is none at typescriptlang.org or in the books I have looked at). For instance "Specify library files to be included in the compilation", which explains absolutely nothing.
Sorted by: Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first) 69
Remember, TS never injects polyfills in your code. It's not its goal. Complementing the accepted anwer:
target tells TS which ES specification you want the final/transpiled code to support. If you configure it as ES5, TS will down compile the syntactic features to ES5, so any arrow functions () => {} in your code will be transformed to function () {}.
Whatever you choose for target affects the default value of lib which in turn tells TS what type definitions to include in your project. If you have "target": "es5", the default value of lib will be ["dom", "es5", "ScriptHost"]. It's assuming which functional features the browser will support at runtime. Adding things to lib it's just to make TS happy - you still need to import the polyfill yourself in the project.
So in short: configure target first, and if you need any extra polyfill in your project OR you know your browser(s) will support this little extra feature, lib is how to make TS happy about it.
Example: You need to support IE11 but also you would like to use promises. IE11 supports ES5, but promises is an ES6 feature. You import a promises polyfill, but TS is still giving an error. Now you just need to tell TypeScript that your code will target ES5 and it's safe to use promises in the codebase:
Typescript does not have any built-in types all types come from a set of base definitions (located in the lib folder in the typescript install directory). By default the target defines which libs are included. For example the docs state:
Note: If --lib is not specified a default list of librares are injected. The default libraries injected are:
► For --target ES5: DOM,ES5,ScriptHost
► For --target ES6: DOM,ES6,DOM.Iterable,ScriptHost
The basic idea is that while target is deals with language features (more specifically which language features need to be down compiled, ex: for-of, or arrow functions), the lib option deals with what facilities the runtime environment has (ie. what built-in objects look like, what they are).
Ideally the default libs for a given target should be used. We may, however, have an environment which supports some of the runtime facilities but not the language features, or we may target runtime with a lower es version and poly-fill some of the runtime facilities, which can be in general done for some things (ex: Promises).
But the libs also influence what features are available on the Typescript side? (Like Promise()) Why doesn't TS just polygons that always? Is that to declare non-TS facilities available in target environment? – Morten H PedersenCommentedJun 23, 2018 at 13:01
2Well, Promise is a type so what I said applies. Typescript tries to have a minimal presence in the generated code. You want polyfills you need to add those yourself – Titian Cernicova-DragomirCommentedJun 23, 2018 at 13:08
+1 For the list of default values. It is a shame that specifying just one library, such as es2021, nullifies the inclusion of everything else. One error corrected, 480 new ones created. This list saved a lot of hassle. – CharlieHansonCommentedJan 3, 2022 at 18:16