JSDoc을 사용하여 보안 웹 구성 요소 입력
나는 우리가 모두 자신이 다음과 같은 상황에 처해 있다는 것을 확신한다. 당신은 매우 기쁘게 인코딩을 하고, 방금 아주 좋은 라이브러리를 찾았는데, 당신을 도울 수 있기 때문에 당신은 그것을 사용하기 시작했다...
import foo from 'foo-lib';
foo.doTheThing(//...
그러나 foo.doTheThing()
은 문자열을 먼저 가져온 다음에 숫자를 가져옵니까 아니면 반대로 가져옵니까?따라서 http://foo-lib.org으로 넘어가서 다섯 번 정도 누르면 함수 서명을 받고 어떻게 사용하는지 알 수 있습니다.우선, 너는 이미 운이 좋았다. 왜냐하면 도서관에 좋은 문서가 별로 없기 때문이다😱
그러나 정보가 워크플로우에 적절하게 접근하지 않았다는 것은 고통스럽게 드러났다.인코딩과 정보 검색을 멈춰야 합니다. 편집기에서 직접 검색할 수 있습니다.😊
그래서 저희가 더 잘할 수 있을 거예요.🤗 아주 간단한 웹 구성 요소부터 시작합시다.
참고: 사용하는 편집기가 VS 코드라고 가정합니다.
같이 놀고 싶으면 - 모든 코드가 github에 있습니다.
<제목 표시줄>
<title-bar>
#shadow-root (open)
<h1>You are awesome</h1>
<div class="dot" style="left: 0px; top: 0px" title="I am dot"></div>
</title-bar>
그것은 단지 하나의 띠일 뿐이다참고: 여기에서는 JavaScript를 사용하지만 대부분의 경우 유형 변환 및 정의 제외) TypeScript의 예는 동일합니다.
import { LitElement, html, css } from 'lit-element';
export class TitleBar extends LitElement {
static get properties() {
return {
title: { type: String },
darkMode: { type: Boolean, reflect: true, attribute: 'dark-mode' },
bar: { type: Object },
};
}
constructor() {
super();
this.title = 'You are awesome';
this.darkMode = false;
this.bar = { x: 0, y: 0, title: 'I am dot' };
this.formatter = null;
}
render() {
// positioning the bar like this is just for illustration purposes => do not do this
return html`
<h1>${this.format(this.title)}</h1>
<div
class="dot"
style=${`left: ${this.bar.x}px; top: ${this.bar.y}`}
title=${this.bar.title}
></div>
`;
}
format(value) {
// we'll get to this later
}
static get styles() {
// we'll get to this later
}
}
customElements.define('title-bar', TitleBar);
당신이 그것을 사용할 때 얻는 것은 무엇입니까
새로 만든 요소를 조회합시다.😊
const el = document.querySelector('title-bar');
여기서, 우리 편집기는 el
이 실제로 무엇인지 모르기 때문에, 더 좋은 코드를 작성하는 데 도움을 줄 수 없습니다.이것은 정보를 사용할 수 있어도 우리 자신의 속성이 코드가 완성되지 않았다는 것을 의미한다.
그래서 우리가 해야 할 일은 그것을 던지는 것이다.
const el = /** @type {TitleBar} */ (document.querySelector('title-bar'));
지금 우리는 이미 자동으로 완성했다.🎉그러나, 우리는 여전히 이런 코드를 작성할 수 있다
el.foo = 'bar';
el.title = true;
아무도 불평하지 않을 것이다.바꿔보도록 하겠습니다.💪
유형 추가 linting
프로젝트에
tsconfig.json
파일 추가{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"lib": ["es2017", "dom"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": false,
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": true
},
"include": [
"src",
"test",
"node_modules/@open-wc/**/*.js"
],
"exclude": [
"node_modules/!(@open-wc)"
]
}
이것이 바로 VS 코드가 코드를 문제가 있는 것으로 표시하는 데 필요한 모든 내용입니다.Property 'foo' does not exist on type 'TitleBar'.
Type 'true' is not assignable to type 'string'.
컨트롤러에서linting과 지속적인 통합을 통해 더욱 실현할 수 있습니다.당신이 해야 할 일은 단지 다음과 같습니다.
npm i -D typescript
이 스크립트를 가방에 추가합니다.json "scripts": {
"lint:types": "tsc"
}
다음을 수행할 수 있습니다.npm run lint:types
이것은 상술한 것과 같은 오류가 발생하지만 파일 경로와 줄 번호가 있습니다.따라서 이러한 추가 작업만 하면 IDE가 유형을 안전하게 유지할 수 있습니다.
솔직히 말하면 이것은 온화한 알림이 아니다. - 빨간색 볼륨은 무시하기 어렵다. 만약에 추가 동력이 필요하다면 F8을 눌러도 된다. 이것은 바로 당신 앞에서 다음 오류를 던질 뿐이다. p.
그것은 어떻게 일합니까?
만약 네가 나처럼, 너는 그것이 어떤 속성이 어떤 유형에 속하는지 어떻게 알고 싶을지도 모른다.나는 물론 아직 어떤 유형도 정의하지 않았다!
Typescript는 ES6 코드를 기반으로 여러 가지 가정을 할 수 있습니다.진정한 마법은 구조 함수에 있다.
constructor() {
super();
this.title = 'You are awesome';
this.darkMode = false;
this.bar = { x: 0, y: 0, title: 'I am dot' };
this.formatter = null;
}
(걱정 마세요. - 포맷 프로그램을 잊지 않았어요. 곧 얘기할 거예요)
유형은 이미 훌륭하지만, 우리는 더 잘할 수 있다.
VS 코드에 있는 인텔리센스를 보세요.
지금 그것은 정말 작다...JSDoc을 추가하겠습니다.
/**
* The title to display inside the title bar
* - should be less then 100 characters
* - should not contain HTMl
* - should be between 2-5 words
*
* @example
* // DO:
* el.title = 'Welcome to the jungle';
*
* // DON'T:
* el.title = 'Info';
* el.title = 'Welcome to <strong>the</strong> jungle';
* el.title = 'We like to talk about more then just what sees the eye';
*/
this.title = 'You are awesome';
많이 좋아지다😊
주의:
@type
을 추가할 필요가 없습니다. 문자열이 분명하기 때문에 추가하면 어느 점에서 동기화되지 않을 수 있습니다.수동으로 유형 설정
하면, 만약, 만약...
this.formatter = null;
이 선만으로는 이 부동산이 무엇을 보유할지 알 수 없다.빈/기본 함수를 할당할 수 있습니다.
this.formatter = value => `${value}`;
그러나 모든 상황에서 의미가 있는 것은 아니다.우리의 예시에서 포맷 프로그램 함수가 없다면, 포맷을 건너뛰고 싶습니다.
기본 함수를 가지고 있으면 그 목적에 위배됩니다.
이 경우
@type
을 제공해야 합니다. JSDoc을 사용하여 수행할 수 있습니다./**
* You can provide a specific formatter that will change the way the title
* gets displayed.
*
* *Note*: Changing the formatter does NOT trigger a rerender.
*
* @example
* el.formatter = (value) => `${value} for real!`;
*
* @type {Function}
*/
this.formatter = null;
이렇게 하면 오류 유형을 제공하면 오류가 표시됩니다.el.formatter = false;
// Type 'false' is not assignable to type 'Function'.
또한 즉시 나타나는 @example
은 자신의 포맷 프로그램을 쉽게 만들 수 있습니다.자신의 유형을 설정하고 사용하다
아직 안 좋아 보이는 집이 하나 더 있다. 바로
bar
집이다.우리의 유형 안전은 이곳에서 이미 작용했다. 이것은 매우 좋지만, 우리는 x가 하나의 숫자라는 것만 안다.다른 정보는 없습니다.
우리도 jsdoc로 이 점을 개선할 수 있다.
그래서 우리는
Bar
이라는 특수한 유형을 정의했다./**
* This is a visible bar that gets displayed at the appropriate coordinates.
* It has a height of 100%. An optional title can be provided.
*
* @typedef {Object} Bar
* @property {number} x The distance from the left
* @property {number} y The distance from the top
* @property {string} [title] Optional title that will be set as an attribute (defaults to '')
*/
이렇게 하면 우리는 일부 속성을 선택할 수 있는 속성으로 정의할 수 있다.우리가 유일하게 해야 할 일은 그것을 분배하는 것이다.
/**
* @type {Bar}
*/
this.bar = { x: 0, y: 0, title: 'I am dot' };
함수 매개 변수에 유형 추가
간단한 형식 함수를 만듭니다. 기본적으로 접두사/접두사를 사용할 수 있습니다. 더 필요하면
formatter
을 덮어쓸 수 있습니다.참고: 이것은 매우 유용한 예는 아니지만 목적을 설명하는 데 충분하다
format(value = '', { prefix, suffix = '' } = { prefix: '' }) {
let formattedValue = value;
if (this.formatter) {
formattedValue = this.formatter(value);
}
return `${prefix}${formattedValue}${suffix}`;
}
마찬가지로 기본 옵션을 사용하면 모든 종류를 알 수 있습니다.따라서 문서를 조금만 추가하면 됩니다.
/**
* This function can prefix/suffix your string.
*
* @example
* el.format('foo', { prefix: '...' });
*/
format(value = '', { prefix = '', suffix = '' } = {}) {
또는 문자열과 숫자를 허용하는 것과 같은 결합 형식을 원한다면.
이 방법을 사용하면 기본 형식을 덮어쓰므로 실제 필요한 내용만 기록해야 합니다. 이것은 동기화되지 않을 수 있습니다.
/**
* This function can prefix/suffix your string.
*
* @example
* el.format('foo', { prefix: '...' });
*
* @param {string|number} value String to format
*/
format(value, { prefix = '', suffix = '' } = {}) {
모든 대상 옵션에 대한 구체적인 설명을 추가해야 한다면, 입력을 복사해야 합니다.
/**
* This function can prefix/suffix your string.
*
* @example
* el.format('foo', { prefix: '...' });
*
* @param {string} value String to format
* @param {Object} opts Options
* @param {string} opts.prefix Mandatory and will be added before the string
* @param {string} [opts.suffix] Optional and will be added after the string
*/
format(value, { prefix, suffix = '' } = { prefix: '' }) {
파일 유형 간 가져오기
파일은 영원히 고립되지 않기 때문에 다른 위치에서 형식을 사용해야 할 수도 있습니다.
우리 오랜 친구의 업무 명세서를 예로 들자.
todo-item.js
및 todo-list.js
을 보유하게 됩니다.이 항목은 이러한 구조 함수를 가지고 있을 것이다.
constructor() {
super();
/**
* What you need to do
*/
this.label = '';
/**
* How important is it? 1-10
*
* 1 = less important; 10 = very important
*/
this.priority = 1;
/**
* Is this task done already?
*/
this.done = false;
}
그렇다면 나는 어떻게 todo-list.js
에서 이런 유형을 다시 사용할 수 있을까?다음 구조를 가정해 보겠습니다.
<todo-list>
<todo-item .label=${One} .priority=${5} .done=${true}></todo-item>
<todo-item .label=${Two} .priority=${8} .done=${false}></todo-item>
</todo-list>
우리는 약간의 통계 데이터를 계산하고 싶다.calculateStats() {
const items = Array.from(
this.querySelectorAll('todo-item'),
);
let doneCounter = 0;
let prioritySum = 0;
items.forEach(item => {
doneCounter += item.done ? 1 : 0;
prioritySum += item.prio;
});
console.log('Done tasks', doneCounter);
console.log('Average priority', prioritySum / items.length);
}
위의 코드는 실제로 오류가 하나 있다😱item.prio
은 존재하지 않습니다.유형은 우리를 구할 수 있었는데, 어때요?먼저 유형을 가져옵니다.
/**
* @typedef {import('./todo-item.js').ToDoItem} ToDoItem
*/
그리고 캐스트를 입력하겠습니다.const items = /** @type {ToDoItem[]} */ (Array.from(
this.querySelectorAll('todo-item'),
));
우리는 이미 유형 오류를 보았다💪데이터 대상을 사용하여 사용자 정의 요소 만들기
대부분의 경우, 우리는 기존 DOM에 접근하여 결과를 유형 변환하기를 원할 뿐만 아니라, 데이터 그룹에서 이러한 요소를 실제로 나타내기를 원한다.
다음은 샘플 배열입니다.
this.dataItems = [
{ label: 'Item 1', priority: 5, done: false },
{ label: 'Item 2', priority: 2, done: true },
{ label: 'Item 3', priority: 7, done: false },
];
그리고 우리는 그것을 과장한다return html`
${this.dataItems.map(
item => html`
<todo-item .label=${item.label} .priority=${item.priority} .done=${item.done}></todo-item>
`,
)}
`;
우리는 어떻게 해야만 이런 유형의 안전을 보장할 수 있습니까?불행하게도
@type {ToDoItem[]}
을 통해서만 전환이 이루어져서는 진정으로 효과가 있을 수 없다😭이것은 이 대상이 HTMLElement의 완전한 표현이기를 기대한다. 물론 우리의 작은 3 속성 대상은 확실히 일부 속성이 부족하다.
우리가 할 수 있는 것은
Data Representation
의 웹 구성 요소를 가지고 있는 것이다.e, g.dom에서 이러한 요소를 만드는 데 필요한 내용을 정의합니다./**
* Object Data representation of ToDoItem
*
* @typedef {Object} ToDoItemData
* @property {string} label
* @property {number} priority
* @property {Boolean} done
*/
그리고 캐스트를 가져오고 입력할 수 있습니다./**
* @typedef {import('./todo-item.js').ToDoItemData} ToDoItemData
* @typedef {import('./todo-item.js').ToDoItem} ToDoItem
*/
// [...]
constructor() {
super();
/**
* @type {ToDoItemData[]}
*/
this.dataItems = [
{ label: 'Item 1', priority: 5, done: false },
{ label: 'Item 2', priority: 2, done: true },
{ label: 'Item 3', priority: 7, done: false },
];
}
화목하다🎉 웹 구성 요소와 데이터의 유형 안전성사용자 유형 사용
만약 형식이 정의 파일이 아니라면, 그것을 어떻게 사용할 수 있는지가 더 어려울 것입니다.
일반적으로 사용자에게 이러한
tsconfig.json
을 추가해야 합니다{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"lib": ["es2017", "dom"],
"allowJs": true,
"checkJs": true,
"noEmit": true,
"strict": false,
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": true
},
"include": [
"**/*.js",
"node_modules/<your-package-name>/**/*.js"
],
"exclude": [
"node_modules/!(<your-package-name>)"
]
}
중요한 부분은 가방 이름의 include
이 아니라 exclude
이다.만약 네가 이것이 좀 복잡하다고 생각한다면, 너는 옳다.이 절차를 개선하기 위해 are ideas이 있지만 최근에는 그다지 주목을 받지 못한 것 같다. 엄지손가락을 세우고 대화를 하는 것이다.
전체 TypeScript 프로젝트에 대해 2
tsconfigs.json
, linting, building 등 더 많은 작업을 해야 할 수도 있습니다.이 방법에 대한 자세한 내용은 Setup For Typescript on Open Web Components에서 확인할 수 있습니다.
빠른 회고:
이러한 속성/기능 옵션을 사용하면 대부분의 웹 구성 요소를 사용할 수 있습니다.
@types
을 추가해야 합니다.전체 코드는 github에서 찾을 수 있습니다.
사용자가 어떻게 얻을 수 있는지 알아보려면 tests을 보십시오.
다음은 뭐예요?
<my-el undefined-attribute>
따라와.웹 구성 요소에 관심이 있으시면 open-wc.org을 보십시오.
Reference
이 문제에 관하여(JSDoc을 사용하여 보안 웹 구성 요소 입력), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/dakmor/type-safe-web-components-with-jsdoc-4icf텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)